Confilcting implementations of Into

pub struct Foo<T>(pub T);

impl<T> Into<T> for Foo<T> {
    fn into(self) -> T {
        self.0
    }
}

gives the confilcting implementations error as Into<T> for T is implemented by default, so Into<Foo> for Foo has multiple confilcting implementations. I think?

So is there a way of doing this, perhaps a way of saying "where T is not Foo"?

edit: it is the impl Into<U> for T where U: From<T> which conflicts

fo example this works:

impl Into<u32> for Foo<u32> {
    fn into(self) -> u32 {
        self.0
    }
}

No, the problem is that this implementation of Into<U> for T where U: From<T> exist. It's not clear to the compiler if it should use your Into<T> implementation for Foo or the generic one from the standard library, which is implemented on Foo as well. This problem is supposed to be solved with specialization, but that won't be stabilized anytime soon (there are still open soundness holes last time I checked).

1 Like

The generally recommended approach is to implement From and let the blanket impl take care of Into.

2 Likes

I tried this in playground when writing a reply and it became immediately obvious why topic starter did not: you cannot write impl<T> From<Foo<T>> for T due to orphan rules. I really should have spotted this without trying code in playground…

OK, thanks, i didn't know that.
although

impl From<Foo<T>> for T {
    fn from(value: Foo<T>) -> Self {
        value.0
    }
}

breaks the orphan rule

Ah didn't look closely enough at the original post.

If you really need a blanket impl for all types the first thing that comes to mind is just provide an inherent into_inner method.

There's not a way around the blanket implementation being conflicting, so you have to avoid that by

  • having implementations for specific types you care about, or
  • using some newtype around T in the other position too, or
    • which doesn't really make sense for your example as that's what Foo<T> already is
  • giving up on From/Into and using your own trait or an inherent method instead

there is the blanket impl Into<U> for T where U: From<T>

which should only produce a conflicting impl Into<T> for Foo<T> if there is an impl From<Foo<T>> for T.

Or will this only be the case when we have specialisation?

Negative reasoning for coherence is only used for local traits/types, as otherwise adding an implementation to your own type could be a breaking change to downstream. So the where clause doesn't really matter in that case.

I don't have time to dig them up right now, but I know there have been other conversations about it with regards to From/Into/TryFrom/TryInto on this forum and elsewhere before (because they are so blanket-implementation encumbered).

1 Like

Ok, makes sense, thank you.

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.