For the educational purpose I am trying to reuse traits From and Into to implement traits FromCoercing and ToCoercing which allows coercive conversion, unlike original From and Into. For simplicity, I used primitive types, but in my version types are not primitive. I know there might be a better mechanism to reach the goal, but point to understanding how to reuse a system of traits to make them do what is required, not finding an optimal way of converting one thing into another coercively.
My understanding says impl< Src : Sized, FromSrc : From< Src > > FromCoercing< Src > for FromSrc does not conflict with impl FromCoercing< u32 > for u16, because there is no implementation of impl From< u32 > for u16. But compiler gives error:
error[E0119]: conflicting implementations of trait `FromCoercing<u32>` for type `u16`
--> src/main.rs:28:1
|
20 | impl< Src : Sized, FromSrc : From< Src > > FromCoercing< Src > for FromSrc
| -------------------------------------------------------------------------- first implementation here
...
28 | impl FromCoercing< u32 > for u16
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u16`
|
= note: upstream crates may add a new impl of trait `std::convert::From<u32>` for type `u16` in future versions
Where is the flaw in my logic and how to make the code working? Any suggestion to read?
There isn't, but one might be defined by std, and then your code would break, merely because a 3rd-party crate (std) added a trait impl. Addition of trait impls is supposed to be a non-breaking change, so the compiler cautiously disallows this kind of impl.
As currently standing, what you want to do can't be achieved. Maybe the very-in-progress specialization feature on nightly could be used here, but I would advise strongly against it even if it did work, exactly because of this kind of ambiguity and lack of backward compatibility.
Let's assume it's not possible to reach the goal by the way. What is your suggestion? How to implement a system of traits making the possible coercive conversion?
You can implement if for specific types manually. Not generically for all types implementing From. You can write a macro to avoid boilerplate code. If you want a trait hierarchy you can add your own alternative to From.
Other than that, unfortunately Rust doesn’t support your case as good as it probably could. While writing subtraits to existing traits form other crates is easy, adding something that’s effectively a supertrait is not always so straightforward.
It’s pretty likely that at some point in the future (yet it can still be a few years) you’ll get the ability to do what you’re trying to to in this example by telling the compiler: “In case there’s ever going to be a u16: From<u32> implementation, I still want my customu16: FromCoercing<u32> implementation to take precedence”.
There’s another option currently that might help in some cases, mainly if your use case for FromCoercing/IntoCoercing is less in trait bounds in where clauses and more in ad-hoc usage. In this case, you could add an extra dummy parameter and two dummy/marker types, e.g. enum ViaFrom {} and enum Custom {}, and then use manual implementations
u16: FromCoercing<u32, Custom>
as well as a generic
impl<T, U> FromCoercing<U, ViaFrom> for T where T: From<U>
With ad-hoc usage of FromCoercing there’s then still the good change that type inference will be able to fill in the necessary dummy parameter so that u16::from_coercing(0_u32) and u32::from_coercing(0_u16) may both work without additional annotation.
Propagating these dummy arguments when appearing in function signatures or similar would however probably be quite annoying, hence my statement that this would work best when
your use case for FromCoercing/IntoCoercing is less in trait bounds in where clauses and more in ad-hoc usage