|
| 1 | +## What is coherence and why do we care? |
| 2 | + |
| 3 | +Coherence means that for any given trait and type, there is one specific |
| 4 | +implementation that applies. This is important for Rust to be easy to reason |
| 5 | +about. When you write `<Foo as Bar>::trait_method`, the compiler needs to know |
| 6 | +what actual implementation to use. |
| 7 | + |
| 8 | +In languages without coherence, the compiler has to have some way to choose |
| 9 | +which implementation to use when multiple implementations could apply. Scala |
| 10 | +does this by having complex scope resolution rules for "implicit" parameters. |
| 11 | +Haskell (when a discouraged flag is enabled) does this by picking an impl |
| 12 | +arbitrarily. |
| 13 | + |
| 14 | +Rust's solution is to enforce that there is only one impl to choose from at all. |
| 15 | +While the rules required to enforce this are quite complex, the result is easy |
| 16 | +to reason about, and is generally considered to be quite important for Rust. |
| 17 | +New features like specialization allow more than one impl to apply, but for any |
| 18 | +given type and trait, there will always be exactly one which is most specific, |
| 19 | +and deterministically be chosen. |
| 20 | + |
| 21 | +An important piece of enforcing coherence is restricting "orphan impls". An impl |
| 22 | +is orphaned if it is implementing a trait you don't own for a type you don't |
| 23 | +own. Rust's rules around this balance two separate, but related goals: |
| 24 | + |
| 25 | +- Ensuring that two crates can't write impls that would overlap (e.g. no crate |
| 26 | + other than `std` can write `impl From<usize> for Vec<i32>`. If they could, |
| 27 | + your program might stop compiling just by using two crates with an overlapping |
| 28 | + impl). |
| 29 | +- Restricting the impls that can be written so crates can add implementations |
| 30 | + for traits/types they do own without worrying about breaking downstream |
| 31 | + crates. |
| 32 | + |
| 33 | + |
| 34 | +## Definitions |
| 35 | + |
| 36 | +Local Trait: A trait which was defined in the current crate. Whether a trait is |
| 37 | +local or not has nothing to do with type parameters. Given `trait Foo<T, U>`, |
| 38 | +`Foo` is always local, regardless of the types used for `T` or `U`. |
| 39 | + |
| 40 | +Local Type: A struct, enum, or union which was defined in the current crate. |
| 41 | +This is not affected by type parameters. `struct Foo` is considered local, but |
| 42 | +`Vec<Foo>` is not. `LocalType<ForeignType>` is local. Type aliases and trait |
| 43 | +aliases do not affect locality. |
| 44 | + |
| 45 | +Covered Type: A type which appears as a parameter to another type. For example, |
| 46 | +`T` is uncovered, but the `T` in `Vec<T>` is covered. This is only relevant for |
| 47 | +type parameters. |
| 48 | + |
| 49 | +Blanket Impl: Any implementation where a type appears uncovered. `impl<T> Foo |
| 50 | +for T`, `impl<T> Bar<T> for T`, `impl<T> Bar<Vec<T>> for T`, and `impl<T> Bar<T> |
| 51 | +for Vec<T>` are considered blanket impls. However, `impl<T> Bar<Vec<T>> for |
| 52 | +Vec<T>` is not a blanket impl, as all instances of `T` which appear in this impl |
| 53 | +are covered by `Vec`. |
| 54 | + |
| 55 | +Fundamental Type: A type for which you cannot add a blanket impl backwards |
| 56 | +compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is |
| 57 | +considered local, `&T`, `&mut T`, and `Box<T>` are also considered local. |
| 58 | +Fundamental types cannot cover other types. Any time the term "covered type" is |
| 59 | +used, the `T` in `&T`, `&mut T`, and `Box<T>` is not considered covered. |
| 60 | + |
| 61 | + |
| 62 | +## Concrete orphan rules |
| 63 | + |
| 64 | +Assumes the same definitions [as above](#definitions). |
| 65 | + |
| 66 | +Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an impl is valid only if at |
| 67 | +least one of the following is true: |
| 68 | + |
| 69 | +- `Trait` is a local trait |
| 70 | +- All of |
| 71 | + - At least one of the types `T0..=Tn` must be a local type. Let `Ti` be the |
| 72 | + first such type. |
| 73 | + - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding |
| 74 | + `Ti`) |
| 75 | + |
| 76 | +We only restrict the appearance of *uncovered* type parameters. Once again, it is |
| 77 | +important to note that for the purposes of coherence, `#[fundamental]` types are |
| 78 | +special. The `T` in `Box<T>` is not considered covered, and `Box<LocalType>` |
| 79 | +is considered local. |
0 commit comments