Rust The Orphan Rule
The orphan rule says an impl is legal only when the trait or the type is local. The newtype pattern is the standard escape hatch for a forbidden impl.
Your trait, their type: allowed
The trait is yours, so you may hand it to any type at all — even the library’s String.
trait Shout {
fn shout(&self) -> String;
}
impl Shout for String {
fn shout(&self) -> String {
format!("{}!", self.to_uppercase())
}
}Their trait, your type: allowed
Display belongs to the library, but Metre is yours. One local name on the line is enough.
struct Metre { value: f64 }
impl fmt::Display for Metre {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} m", self.value)
}
}Their trait, their type: forbidden
Neither name is yours. So the impl is refused with error E0117. The message reads: “only traits defined in the current crate can be implemented for types defined outside of the crate”.
impl fmt::Display for Vec<String> {
// refused — Display is the library’s, and so is Vec
}A local type anywhere on the line can save it
From and String are both the library’s — but Metre, named inside the impl, is yours, so this one passes.
impl From<Metre> for String {
fn from(m: Metre) -> String {
format!("{} m", m.value)
}
}The escape hatch: wrap it in a newtype
When the forbidden impl is the one you need, wrap the foreign type in your own struct. The wrapper is yours, so Display may be implemented for it.
struct NameList(Vec<String>);
impl fmt::Display for NameList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.join(", "))
}
}Example
use std::fmt;
// Your trait — you may give it to any type, even the library’s.
trait Shout {
fn shout(&self) -> String;
}
impl Shout for String {
fn shout(&self) -> String {
format!("{}!", self.to_uppercase())
}
}
// Your type — you may give it any trait, even the library’s.
struct Metre {
value: f64,
}
impl fmt::Display for Metre {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} m", self.value)
}
}
// A local type anywhere in the impl line can make it legal:
// From and String are both the library’s, but Metre is yours.
impl From<Metre> for String {
fn from(m: Metre) -> String {
format!("{} m", m.value)
}
}
// The newtype escape hatch: wrap the foreign type in your own.
struct NameList(Vec<String>);
impl fmt::Display for NameList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.join(", "))
}
}
fn main() {
let word = String::from("hello");
println!("{}", word.shout());
let walk = Metre { value: 2.5 };
println!("walk = {}", walk);
let label: String = Metre { value: 4.0 }.into();
println!("label = {}", label);
let team = NameList(vec![String::from("Ada"), String::from("Bo")]);
println!("team = {}", team);
}Key points
- Coherence is the guarantee that a trait has at most one impl for any type, anywhere.
- The orphan rule gives it, together with the ban on overlapping impls (E0119).
- Why the rule exists: without it, two crates could give the same type two different impls.
- Your program could then break just by adding a dependency.
- The rule lets library authors add blanket impls safely.
- impl<T: Display> MyTrait for T can never collide with an impl a user wrote for one type.
- Fundamental types see through the wrapper.
- Box<Metre> counts as local because Metre is.
- The same holds for &Metre and &mut Metre.
- Inherent impls are stricter still.
- A plain impl Metre { ... } method block must live in the crate that defines Metre.
- If you skip Clone, your users can never add it for you.
- An impl needs one local name on its line: your trait or your type.
- Rust Traits stated this rule in one line.
In this example, every impl line owns at least one name. Shout is our trait, so String may have it. Metre is our type, so it may have the library’s Display. Metre’s presence also saves From<Metre> for String. That holds even though From and String are both foreign. NameList shows the escape hatch: Vec<String> itself can never receive our Display, but our wrapper around it can. The one shape missing here is the forbidden one — foreign trait, foreign type — which you saw refused above.
Note
The orphan rule is also why common traits are derived eagerly on published types.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Which of these impls does the orphan rule allow?