Trait Coherence
Trait coherence is the global type-system property guaranteeing that for any given trait Trait<P1, ..., Pn> and any concrete self type T, there is at most one implementation of that trait across the entire compiled program. Coherence prevents the "hash-table problem" and linking ambiguities where two dependency crates define conflicting implementations of the same trait for the same type.
Why Global Uniqueness of Trait Implementations Matters
Without trait coherence, two upstream crates (crate_a and crate_b) could independently implement Hash or Ord for a standard type (or a shared third-party type). If crate_a inserted items into a BTreeMap<SharedKey, Value> using its Ord implementation and passed that map to crate_b, crate_b would look up keys using a different ordering, corrupting the B-tree invariants!
What Coherence Prevents:
+--- crate_a defines `impl Ord for Point` (sorts by x, then y)
shared |
`Point` +--- crate_b defines `impl Ord for Point` (sorts by y, then x)
|
v
Final Binary links both `crate_a` and `crate_b`:
Which `Ord` does `BTreeMap<Point, V>` use? ---> Prevented at compile time by Coherence!
The Two Rules That Enforce Coherence
Rust enforces trait coherence using two complementary compile-time checks:
| Rule | Error Code | What It Checks |
|---|---|---|
| 1. Non-Overlap Rule | E0119 |
Within a crate (and across visible upstream crates), no two impl blocks for the same trait may apply to a common concrete type substitution |
| 2. Orphan Rule | E0117 |
An impl Trait<P1..Pn> for T block is allowed only if Trait or at least one uncovered type in the header is defined in the current crate |
Overlap Checking and "Negative Reasoning" About Upstream Crates (E0119)
When checking whether two implementations overlap, rustc assumes that an upstream crate is allowed to add new trait implementations for its own types in future minor releases without breaking your crate.
Consequently, even if upstream std::string::String does not implement Copy, writing both impl<T: Copy> MyTrait for T and impl MyTrait for String in your crate is rejected (E0119) if String is treated as an upstream type that could theoretically implement the bound—or more generally whenever a blanket impl<T: UpstreamTrait> LocalTrait for T coexists with impl LocalTrait for UpstreamType:
// ❌ does not compile
trait Summarize {
fn summary(&self) -> String;
}
impl<T: Clone> Summarize for T {
fn summary(&self) -> String {
String::from("cloned summary")
}
}
// Overlaps with `impl<T: Clone> Summarize for T` because `u32` implements `Clone`!
impl Summarize for u32 {
fn summary(&self) -> String {
format!("u32: {}", self)
}
}
fn main() {}
error[E0119]: conflicting implementations of trait `Summarize` for type `u32`
--> src/main.rs:13:1
|
6 | impl<T: Clone> Summarize for T {
| ------------------------------ first implementation here
...
13 | impl Summarize for u32 {
| ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
Distinct Generic Type Parameters Make Implementations Disjoint
Two implementations of the same trait for the same self type T are completely coherent if their trait type parameters cannot unify:
trait ConvertFrom<Src> {
fn convert(src: Src) -> Self;
}
impl ConvertFrom<u16> for u64 {
fn convert(src: u16) -> Self {
u64::from(src)
}
}
impl ConvertFrom<u32> for u64 {
fn convert(src: u32) -> Self {
u64::from(src)
}
}
fn main() {
let a = <u64 as ConvertFrom<u16>>::convert(10_u16);
let b = <u64 as ConvertFrom<u32>>::convert(20_u32);
println!("a = {}, b = {}", a, b);
}
a = 10, b = 20