PSA: The order of your trait's type parameters affects coherence rules

Quoted from E0210 on the Error Index:

For another example of an error, suppose there's another trait defined in foo named ForeignTrait2 that takes two type parameters. Then this impl results in the same rule violation:

struct MyType2;
impl<T> ForeignTrait2<T, MyType<T>> for MyType2 { } // error

The reason for this is that there are two appearances of type parameter T in the impl header, both as parameters for ForeignTrait2. The first appearance is uncovered, and so runs afoul of the orphan rule.

Consider one more example:

impl<T> ForeignTrait2<MyType<T>, T> for MyType2 { } // Ok

This only differs from the previous impl in that the parameters T and MyType<T> for ForeignTrait2 have been swapped. This example does not violate the orphan rule; it is permitted.

To see why that last example was allowed, you need to understand the general rule. Unfortunately this rule is a bit tricky to state. Consider an impl:

impl<P1, ..., Pm> ForeignTrait<T1, ..., Tn> for T0 { ... }Run

where P1, ..., Pm are the type parameters of the impl and T0, ..., Tn are types. One of the types T0, ..., Tn must be a local type (this is another orphan rule, see the explanation for E0117). Let i be the smallest integer such that Ti is a local type. Then no type parameter can appear in any of the Tj for j < i.

For information on the design of the orphan rules, see RFC 1023.

TL;DR: when you define a trait, the order of its type parameters has an impact on coherence rules, and will decide whether certain impls in foreign crates are possible.

When I read this, by brain suddenly rebooted and I immediately lost all capacity to reason.

In light of this, can anyone offer advice with regards to what order we should write our type parameters in when we define a public trait?

5 Likes

@nikomatsakis did a nice writeup on this.

3 Likes