Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Orphan Rule

Goal of This Episode

Understand Rust’s orphan rule, and what to do when you want to implement an external trait for an external type.

Concept

In Chapter 5 we learned traits — you can implement any trait for your own types. But have you ever tried this:

use std::fmt;

impl fmt::Display for Vec<i32> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "my vec")
    }
}

fn main() {}

The compiler refuses flat out. Why?

The Orphan Rule

Rust has a rule:

To impl a trait, at least one of the trait or the type must be defined in your own crate.

Put differently: the trait is yours, or the type is yours — at least one must hold.

In the example above, Display is defined by the standard library, and so is Vec<i32> — neither is yours, so no.

Why This Restriction Exists

Imagine there were no orphan rule:

  • crate A implements Display for Vec<i32>, printing [1, 2, 3].
  • crate B also implements Display for Vec<i32>, printing 1 | 2 | 3.
  • Your program uses both A and B… which should the compiler pick?

That’s a conflict. The orphan rule prevents the problem at its root.

All of these are legal:

// Case 1: your type + an external trait
struct MyPoint {
    x: f64,
    y: f64,
}

impl std::fmt::Display for MyPoint {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

// Case 2: an external type + your trait
trait Describable {
    fn describe(&self) -> String;
}

impl Describable for Vec<i32> {
    fn describe(&self) -> String {
        format!("A Vec with {} elements", self.len())
    }
}

fn main() {}

Adding Methods to an External Type with Your Own trait

Case 2 above is especially useful. Although Vec<i32> is a standard-library type, Describable is a trait we defined in the current crate, so the orphan rule allows us to write impl Describable for Vec<i32>. After that implementation, a Vec<i32> value can call .describe() with method syntax.

Why Can’t You Write impl Vec<i32> Directly?

If the goal is merely to add a method, you might wonder why we cannot skip the trait and write this instead:

impl Vec<i32> {
    fn describe(&self) -> String {
        format!("A Vec with {} elements", self.len())
    }
}

fn main() {}

Rust does not allow this either. The Type in impl Type { ... } must be defined in the current crate. Vec is defined by the standard library; bringing it into scope with use does not make it your type.

The reason is the one behind the orphan rule — two crates colliding — except that with a trait the collision stays distinguishable. A trait method is only callable when its trait is in scope, so even if crate A and crate B each define their own trait and implement describe for Vec<i32>, the decision is still yours: whichever trait you use, that is the .describe() you get.

A method written directly inside impl Vec<i32> { ... } has no such layer. It belongs to no trait, and calling it requires no use at all: as long as that crate is among your dependencies, v.describe() simply exists. If two crates each added one, you would have no way to say which one you meant. That is why a method attached directly to a type can only be added by the crate that defines that type.

The boundary here is the crate, not a particular file or mod. As long as a type is defined in the current crate, its impl Type { ... } block may live in another mod within that same crate.

Therefore, when you do not want to wrap an external type but do want to add a method to it, you can define your own trait and implement that trait for the external type, as above.

The Newtype Pattern (the Workaround)

If you truly need to implement an external trait for an external type, use the newtype pattern — a tuple struct wrapping the external type:

use std::fmt;

struct MyVec(Vec<i32>);

impl fmt::Display for MyVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let items: Vec<String> = self.0.iter()
            .map(|x| x.to_string())
            .collect();
        write!(f, "[{}]", items.join(", "))
    }
}

fn main() {}

MyVec is a type you defined, so implementing Display for it is allowed. self.0 reaches the inner Vec<i32>.

Example Code

use std::fmt;

// The newtype pattern: wrapping an external type in your own struct
struct Scores(Vec<i32>);

impl Scores {
    fn new() -> Scores {
        Scores(Vec::new())
    }

    fn add(&mut self, score: i32) {
        self.0.push(score);
    }

    fn total(&self) -> i32 {
        self.0.iter().sum()
    }
}

// Now Display can be implemented for "your type"
impl fmt::Display for Scores {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let items: Vec<String> = self.0.iter()
            .map(|x| x.to_string())
            .collect();
        write!(f, "Scores: [{}], total: {}", items.join(", "), self.total())
    }
}

fn main() {
    let mut scores = Scores::new();
    scores.add(85);
    scores.add(92);
    scores.add(78);
    scores.add(95);

    // With Display implemented, println works directly
    println!("{}", scores);
}

The Multi-parameter trait Case

The rule above is the simplest version. For multi-parameter traits (like Chapter 5’s From<T>), the rules are actually more intricate. In brief:

// OK: your type appears among the parameters
impl From<MyType> for String { ... }

// Not allowed: both sides are external
impl From<String> for Vec<i32> { ... }

The complete rules involve concepts like “covered type parameters,” beyond this tutorial’s scope. The curious can consult the official documentation.

Recap

  • The orphan rule: in impl Trait for Type, at least one of the trait or the type must be defined in your crate.
  • “Your type + an external trait” ✅ legal.
  • “An external type + your trait” ✅ legal.
  • “An external type + an external trait” ❌ illegal.
  • Defining your own trait and implementing it for an external type adds methods to that type.
  • The Type in impl Type { ... } must be defined in the current crate, though the impl and the type may live in different files or mods.
  • The orphan rule exists to prevent impl conflicts between crates.
  • The newtype pattern: wrap the external type in struct MyWrapper(OriginalType), and it becomes your type.
  • The orphan rule for multi-parameter traits is far subtler — see the official documentation.