What exactly is "orphan impl"?

Hey! Familiarizing myself with "orphan rules" I see people often use "orphan impl" term. What exactly is "orphan impl"?

Variants:

  • impl ExternalTrait for ExternalType
  • impl InternalTrait for ExternalType
  • impl ExternalTrait for InternalType
  • impl InternalTrait for InternalType
  • some combinations of the above
1 Like

If you’re talking about a concrete type and a concrete trait, an orphan impl is only an

Once generics get involved (on the type, the trait, and/or the impl), the picture is a bit more complex.

5 Likes

The general idea behind orphan rules is that conflicting implementations must be forbidden. If crate a is used in crates b and c, and crate a defines a type Foo and a trait Bar, then crate b must not create an impl Bar for Foo: If it could do this, then crate c could do the same thing; ultimately a crate d depending on both b and c would see two conflicting implementations.

That’s why impl ExternalTrait for ExternalType are forbidden. The other cases can’t produce this problem: at least in a world without generics, for every impl there’s always only exactly one crate that could create that impl: If a trait Bar and type Foo are defined in the same crate, only that crate can impl the trait for the type, i.e. impl Bar for Foo. If they’re defined in different crates, then impl Bar for Foo can only be in one of the two crates, depending on which one of the crates depends on the other. Cyclic dependencies are impossible, so two conflicting impls are ruled out; if neither crate depends on the other, nobody can write an impl Bar for Foo.

9 Likes

I believe that impl ExternalTrait for ExternalType is called an "orphan" because it is separated from both of its "parents" (ExternalTrait and ExternalType), which live in other crates.

7 Likes

If you want the precise rules, they are described here. See also this repo for some longer form description and history.

1 Like

This repo was a source of confusion for me because it operates the term but doesn't explain it beforehand. In RFC you provided the term is defined clearly:

An impl is orphaned if it is implementing a trait you don't own for a type you don't own.

Thanks to everyone! Now it's clear.

It's more complex when generics are involved, as others mentioned. E.g. this is not an orphan impl:

struct MyType;
impl From<MyType> for Vec<String> {
    fn from(_: MyType) -> Self {
        Vec::new()
    }
}

Even though From and Vec are both foreign. The RFC goes into more detail.

(But "implementing a trait you don't own for a type you don't own" is the standard approximation thrown around for brevity and intuition's sake.)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.