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;
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 defineIntoFoo
trait(s), for various reasons.