`:T1 != T2` (type non-equality) bound

Standard library offers a very convenient and natural implementation of From<T> for T.

This implementation, however, sometimes creates problems for Rust developers who want to implement From because of conflicting implementations.

One notable example of this is how anyhow::Error cannot implement std::error::Error.

Or, in general, if one wants to define a type-wrapper Wrapper<T> and wants to permit reasonable behavior where Wrapper<T> can be converted into Wrapper<U> via From/Into as long as T can be converted into U, this is not possible either.

This is because the case of impl<T,U> From<Wrapper<T>> for Wrapper<U> for T=U is covered by the implementation from the standard library and Rust currently does not support specialization.

At the moment of writing, there's no way to to express that the implementation would apply only to T != U.

Let's say I would like to implement such a bound in rustc. Where would I be able to do this?

1 Like

After thinking for a while, I realize that it could also be the mechanism for having an autotrait-like behavior with a limited set of types-implementors.

You mean the From implementation for impl<T, U: From<T>> From<Wrapper<T>> for Wrapper<U> should carve out the T = U case in order to have the (default) impl<W> From<W> for W (for W = Wrapper<T> = Wrapper<U>) apply there?

That sounds a lot like specialization[1] and intuitively, I’d say such a T != U-bounds feature – the way you’re imagining it (judging by your suggested use-cases) shares (at least in the general case[2]) the same tricky-or-impossible-to-solve soundness problems.


  1. except even more general, supporting resolution of a partially-covered/overlapped with another impl instead of “just” fully-covered/overlapped (i.e. strictly more specific) one ↩︎

  2. I believe for anyhow::Error, the soundness issues could probably be avoided, as we’re talking about a single, concrete, and crucially lifetime-parameter-free/: 'static type anyhow::Error which gets special treatment carved out by the E != anyhow::Error bound that one would use here ↩︎

3 Likes

I'm pretty sure this is equivalent to specialization in its troublesomeness, or even more so as it breaks lifetime erasure. Think about writing code like

struct Foo<'a> {...}

trait MyFrom<T> {...}
impl MyFrom<T> for T {...}
impl MyFrom<Foo<'static>> for T where T != Foo<'static> {...}

fn convert<'a>(input: Foo<'static>) -> Foo<'a> {
    MyFrom::from(input)
}

Now convert must be compiled to one of the two impls, depending on whether 'a equals 'static or not, and I didn't even write 'a != 'static.

3 Likes

Technically this is specialization, but many impl<T,U> From<Wrapper<T>> for Wrapper<U> has same logic (identity) with impl<T> From<T> for T when T == U. I wonder is there a way to permit this,