ProgrammingRust Developer, API/Library Designer

Explain how the system of traits works in Rust when dealing with external types (orphan rule). How to implement a trait for a type that you didn't declare, and why is it not always possible?

Pass interviews with Hintsage AI assistant

Answer

In Rust, implementing a trait for a type is allowed only if:

  • the trait is declared in the current crate OR
  • the type is declared in the current crate

These conditions are called the orphan rule. This protects against conflicts in the case of independent implementations of the same trait for the same type in different crates.

If you want to implement an external trait for an external type — this is not possible directly. Typically, a "newtype" pattern is used: wrap the type in your own structure within the crate, and then implement the trait for your own type.

Example:

// External type and external trait (not in our crate) // struct ExternalType; trait ExternalTrait { ... } struct MyWrapper(ExternalType); impl ExternalTrait for MyWrapper { /* ... */ } // Now we work through the wrapper

Trick question

Question: Can you implement an external trait for an external type if both are public, through use and impl?

Incorrect answer: Yes, it’s enough to simply declare impl in your crate, and everything will work.

Correct answer: No, the compiler will not allow that. You can only implement a trait if either the trait or the type is declared in the current crate. Otherwise — orphan rule error.

Example of an error:

// extern crate other; // impl Display for other::ExternalType { ... } // Orphan rule error

Examples of real errors due to lack of knowledge of the nuances of the topic


Story

The team needed support for an external logger through impl of an external trait. Attempting to implement it for a third-party type led to an orphan rule error and lengthy debates about the reasons. Ultimately, they had to redesign the architecture to use newtype.


Story

In an open-source project, they attempted to implement Debug for a structure from another library, which was blocked by orphan rule. As a result, users had to deal with awkward workarounds and write their own wrappers.


Story

A serious bug: when trying to implement FromStr for an external enum to parse from a string, the compiler blocked it due to orphan rule. The developer "solved" the problem via unsafe memory conversions, which caused UB in production.