Trait Coherence
Trait coherence is the property that for any given trait and any given concrete type (plus trait type arguments), there is at most one implementation of that trait across the entire compiled program.
The Two Pillars of Coherence
Rust enforces coherence at compile time through two complementary rules:
Trait Coherence
|
+---------------------+---------------------+
| |
1. Non-Overlap Rule (E0119) 2. Orphan Rule (E0117 / E0210)
No two `impl` blocks in the same Downstream or sibling crates cannot
crate graph may apply to the same add `impl ExternalTrait for ExternalType`
concrete type and trait combination. that could collide at link time.
Overlapping Implementations (E0119)
Two impl blocks overlap if there exists even a single concrete type (now or in a future upstream version) that could match both implementations simultaneously.
1. Blanket Implementation vs. Concrete Implementation
If you provide a blanket implementation impl<T: Display> Describe for T, you cannot also write a specialized impl Describe for String, because String implements Display and therefore matches both impl blocks:
// ❌ does not compile
use std::fmt::Display;
trait Describe {
fn describe(&self) -> String;
}
impl<T: Display> Describe for T {
fn describe(&self) -> String {
format!("display: {self}")
}
}
impl Describe for String {
fn describe(&self) -> String {
format!("string: {self}")
}
}
fn main() {}
error[E0119]: conflicting implementations of trait `Describe` for type `String`
--> src/main.rs:14:1
|
8 | impl<T: Display> Describe for T {
| ------------------------------- first implementation here
...
14 | impl Describe for String {
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `String`
2. Two Blanket Implementations with Different Trait Bounds
Even if no type in your crate currently implements both TraitA and TraitB, writing both impl<T: TraitA> Target for T and impl<T: TraitB> Target for T is rejected by E0119 because any type could implement both TraitA and TraitB simultaneously:
// ❌ does not compile
trait FastHash {}
trait CryptoHash {}
trait Digest {}
impl<T: FastHash> Digest for T {}
impl<T: CryptoHash> Digest for T {}
fn main() {}
error[E0119]: conflicting implementations of trait `Digest`
--> src/main.rs:7:1
|
6 | impl<T: FastHash> Digest for T {}
| ------------------------------ first implementation here
7 | impl<T: CryptoHash> Digest for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
Negative Reasoning and Upstream SemVer Hazard
Rust's coherence checker also considers future upstream changes. Suppose ExternalType from std does not currently implement Display. If you write:
impl<T: Display> MyTrait for Timpl MyTrait for ExternalType
The compiler still rejects this pair if ExternalType is an upstream type that might add impl Display for ExternalType in a future minor release, because adding a trait implementation in an upstream crate must not silently break downstream crates.
Designing Conflict-Free Generic Implementations
When you want both blanket behavior for groups of types and specialized behavior for individual types without violating coherence, wrap the blanket category in a dedicated wrapper struct:
use std::fmt::Display;
trait Render {
fn render(&self) -> String;
}
// Wrap any Display type explicitly in `ViaDisplay<T>` instead of a bare `impl<T: Display> Render for T`:
struct ViaDisplay<T>(pub T);
impl<T: Display> Render for ViaDisplay<T> {
fn render(&self) -> String {
format!("{}", self.0)
}
}
// Now `String` (or any other type) can have its own custom `Render` implementation without conflict:
impl Render for String {
fn render(&self) -> String {
format!("{self:?}")
}
}
fn main() {
println!("ViaDisplay(42): {}", ViaDisplay(42).render());
println!("String direct: {}", String::from("hello").render());
}
ViaDisplay(42): 42
String direct: "hello"