Home > Back-end >  How do I idiomatically implement From<A<T>> for A<U> without conflict?
How do I idiomatically implement From<A<T>> for A<U> without conflict?

Time:01-25

I have created a generic, monadic type -- call it A<T>. I would like to be able to have this type convert safely between A<T> to A<U> if U: From<T>, while still encapsulating it within an A<_>, ideally without having to expose or use a T directly.

What I would like to do, is to implement From<A<T>> constrained with U: From<T>; however the following code does not compile:

struct A<T>{
    val: T
}

impl<T, U> From<A<T>> for A<U>
    where U: From<T>
{
    fn from(other: A<T>) -> A<U> {
        A{ val: U::from(other.val) }
    }
}

The above fails to compile due to the generic implementation impl<T> From<T> for T defined in core. The full error is:

error[E0119]: conflicting implementations of trait `std::convert::From<A<_>>` for type `A<_>`
 --> src/lib.rs:5:1
  |
5 | impl<T, U> From<A<T>> for A<U>
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> From<T> for T;

Demo

The reason for why this fails is clear: this definition applies for cases where T == U, which would make this impl From<A<T>> for A<T> which is the same as From<T> for T -- and thus, a conflict. However what is not clear is what the idiomatic approach is to overcome this -- since I cannot imagine I am the only one to want to keep a type fully encapsulated.

Is this possible to achieve without needing unstable features like negative trait-bounds or trait-specializations? If not, what is the general, idiomatic approach to support a From conversion while still encapsulating the type? Am I stuck implementing a function that behaves exactly like fn from(...), but without implementing the trait?

CodePudding user response:

There is no way to make

impl<T, U> From<A<T>> for A<U>
    where U: From<T>

succeed. (Even if a future version of Rust offers specialization, it would only work here if the built-in From<T> for T impl were marked as allowing specialization.)

Your choices are:

  • Implement the conversion for concrete non-equal types — From<A<Bar>> for A<Foo>. (I assume this is not desirable for your application, but you seem interested in a broad answer.)

  • As you already thought of, “implementing a function that behaves exactly like fn from(...), but without implementing the trait”. I'd write it like:

    impl<T> A<T> {
        fn convert<U>(self) -> A<U> where T: Into<U> {
            A { val: self.val.into() }
        }
    }
    

    But note that if your monadic type has a map() method, then this is just .map(Into::into).

  • Define your own conversion trait, that is not From.

    There's probably no reason to do this in your case, unless you have other types similar to A, but considered broadly, this is actually fairly common; many libraries define IntoFoo trait(s), for various reasons.

  • Related