A confusing restriction on orphan rules

Let's take an example with the newtype fashion below:


struct Wrap<T>(T);

impl From<Wrap<i32>> for i32
{
    fn from(v: Wrap<i32>) -> i32 { v.0 }
}

fn main()
{
    let w:Wrap<i32> = Wrap::from(2);
    println!("{}", w.0);
}

Above codes works fine. And here, Wrap<i32> has been identified to local type, and impl trait<localtype> for foreigntype is allowed by rust.

But if we change i32 into its generic form, rust will raise compiler error.

impl From<Wrap> for T
^ type parameter T must be covered by another type when it appears before the first local type (Wrap<T>)

struct Wrap<T>(T);

impl<T> From<Wrap<T>> for T
{
    fn from(v: Wrap<T>) -> T { v.0 }
}

fn main()
{
    let w:Wrap<i32> = Wrap::from(2);
    println!("{}", w.0);
}

This is confusing because:

  1. I believe the intention here for a generic implementation to newtype make a lot of sense here.
  2. For generic type T, it could be any type: local type or foreign type, where foreign type has more restriction. If we allowed foreign type here, logically we should also allow local type and the combination of local type and foreign type (the generic type).

A very similar question was answered here and the explanation is better than I could give you.

1 Like

I can write this today.

trait Trait {}
struct S;
impl<T: std::fmt::Debug> Trait for T {}
impl<T: Trait> From<T> for S {
    fn from(_: T) -> Self {
        S
    }
}

And you can implement Debug for Wrap<T> even if T is not Debug.

Then your desired blanket implementation would conflict with my allowed, existing implementation.

(Edit: you don't need the local Trait, I just failed to minimize the example.)

1 Like

Thank you for sharing the information. It seems the orphan rule is much more restricted to generic types.

1 Like

In effect, yes; coherence performs negative reasoning locally, say.

But generic parameters aren't local.

1 Like