Orphan Rules in Depth
The orphan rule (E0117) is the crate-boundary coherence rule that forbids implementing a foreign trait for a foreign type. In its full RFC 2451 formulation (rebalanced coherence), an implementation impl<P1..=Pn> ForeignTrait<T1..=Tm> for T0 is legal only if at least one local type appears in T0..=Tm before any uncovered generic type parameter Pi.
Formal Definition of Covered vs. Uncovered Type Parameters
Given impl<P1..=Pn> Trait<T1..=Tm> for T0:
- A type parameter
Piis uncovered if it appears directly asT0or one ofT1..=Tm(or behind a fundamental type like&Pi,&mut Pi, orBox<Pi>), rather than wrapped inside a non-fundamental type constructor likeVec<Pi>orLocalWrapper<Pi>. - When
Traitis a foreign trait (such asFrom,TryFrom,Add, orDisplay), at least one local typeLocalTypemust appear in the sequenceT0, T1, ..., Tmstrictly to the left of the first uncovered type parameterP.
Sequence of Positions in `impl<T> ForeignTrait<T1, T2> for T0`:
Position 0: `T0` (Self type)
Position 1: `T1` (1st trait parameter)
Position 2: `T2` (2nd trait parameter)
Rule: Moving left-to-right (T0 -> T1 -> T2), you MUST hit a `LocalType`
BEFORE hitting an uncovered generic parameter `T`!
Examples of Legal vs. Illegal Foreign Trait Implementations
Suppose Local is a struct defined in the current crate and From<T> is the foreign trait from std::convert:
impl Header |
Position T0 (Self) |
Position T1 |
Legal under Orphan Rule? | Reason |
|---|---|---|---|---|
impl From<u32> for String |
String (foreign) |
u32 (foreign) |
No (E0117) |
No local type appears anywhere |
impl<T> From<T> for Local |
Local (local) |
T (uncovered) |
Yes | Local appears at T0, before T at T1 |
impl<T> From<Local> for Vec<T> |
Vec<T> (T is covered) |
Local (local) |
Yes! | T is covered inside Vec<T>, and Local appears at T1 |
impl<T> From<Local> for T |
T (uncovered!) |
Local (local) |
No (E0210) |
Uncovered T appears at T0, before Local at T1 |
// ❌ does not compile
struct LocalWrapper(u32);
// Rejected (E0210): uncovered type parameter `T` appears at `T0` (Self)
// BEFORE `LocalWrapper` appears at `T1`!
impl<T> From<LocalWrapper> for T {
fn from(_: LocalWrapper) -> T {
todo!()
}
}
fn main() {}
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalWrapper`)
--> src/main.rs:6:6
|
6 | impl<T> From<LocalWrapper> for T {
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalWrapper`)
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
Why Covered Type Parameters (Vec<T>) Are Allowed Before Local
In impl<T> From<LocalList<T>> for Vec<T>, the self type T0 is Vec<T> (where T is covered by Vec), and the first trait parameter T1 is LocalList<T> (a local type). Because T is covered in T0 and a local type appears at T1, this implementation is completely legal in stable Rust!
struct LocalPair<T>(T, T);
// Legal! `T` in `Vec<T>` is covered by `Vec`, and `LocalPair<T>` is a local type at `T1`:
impl<T> From<LocalPair<T>> for Vec<T> {
fn from(pair: LocalPair<T>) -> Self {
vec![pair.0, pair.1]
}
}
fn main() {
let pair = LocalPair("first", "second");
let vec: Vec<&str> = Vec::from(pair);
println!("converted to foreign Vec<T>: {:?}", vec);
}
converted to foreign Vec<T>: ["first", "second"]
Fundamental Types (&T, &mut T, Box<T>)
References (&T, &mut T) and Box<T> are marked #[fundamental] in the standard library. Unlike Vec<T>, a #[fundamental] type constructor does not cover its inner type parameter: Box<T> is treated as uncovered T, whereas Box<LocalType> is treated as a local type (so impl ForeignTrait for Box<LocalType> is allowed!).