The error is misleading and incorrect in some sense; see issue 40120. Applying the suggestions does fix the issue, but arguably in a suboptimal way.
However, it still contains useful information even if you consider it to be incorrect. Let's start from here and try to trace the first error message's logic. It's pointing out this blanket implementation:
89 | impl<Original, Target> From2<Original> for Target
Which is indeed dependent on AsNumber
:
where
Original: AsNumber,
Target: AsNumber,
And it says that's because of the declaration of Into2
:
28 | pub trait Into2<T>: Sized
| ----- required by a bound in this
29 | where
30 | T: From2<Self> + Sized,
| ^^^^^^^^^^^ required by this bound in `Into2`
And Into2
is a (required) supertrait of FromInto
. It suggests binding Self
and T
by AsNumber
.
If we consider these notes backwards starting from this trait we've declared, FromInto
, it goes something like
FromInto<T>
requires Into2<T>
Into2<T>
in turn requires that
This is probably where the diagnosis should stop, but it instead continues and tries to see what it would take for T: From2<Self> + Sized
and finds the blanket implementation. The requirements of the blanket implementation, translated, are
-
T: AsNumber
Self: AsNumber
And this is the source of the suggestions. If you apply the suggestions, it does in fact fix the errors.
- The blanket implementation of
From2
shows that T: From2<Self>
- This satisfies the
where
clause on trait Into2
- This satisfies the supertrait bound on
trait FromInto
I'll circle back to the commented-out implementation of FromInto
, but first...
...let's back up. I asserted that the diagnosis probably should have stopped here:
FromInto<T>
requires Into2<T>
Into2<T>
in turn requires that
And the reason is that, like described in the issue I linked above, you can satisfy these bounds directly, and this is often more general than relying on the "far away" blanket implementation. So let's update our new bounds to use this "closer" requirement:
pub trait FromInto<T>: Into2<T>
where
- Self: AsNumber,
- T: AsNumber,
+ T: From2<Self>,
{
Yep, that works to.
What about the commented out implementation of FromInto
? So long as you add the same restrictions as you did to the trait declaration, it works now too.
A lot of it is just experience; once you've encountered it enough, you'll probably build up a feel for these situations, and an ability to sort of trace out what logic chain the compiler was going down when it gave you the error that it did (like I wrote up above).
However, there is one piece of critical knowledge to help understand these bounds that you have to carry around on your trait declarations and implementations:
- Implied bounds need not be repeated
- But non-implied bounds have to be repeated
- Supertrait bounds are implied
- But bounds are parameters (like the
T
in Into2<T>
) are not implied (yet)
- And these are the ones you have to "carry around"
So if we consider this:
pub trait FromInto<T>: Into2<T>
We can automatically assume any supertrait bounds of
pub trait Into2<T>: Sized
where
T: From2<Self> + Sized,
And there is one: We can assume that Self: Sized
. We don't have to repeat that bound, even though it's required. However, we cannot assume that
Because that's not a supertrait bound. (We can't assume that T: Sized
either, but because we didn't use T: ?Sized
, it has that bound anyway. Yes, this implied bounds can interact in confusing ways.)
And so that's why you had to add the bound to your trait declaration in order for it to be consistent.
There's a plan to expand implied bounds to cover bounds on type parameters such as this. If they pull it off , it will remove the need for "carrying around" these non-supertrait bounds like you've encountered here. You can learn more by reading the RFC; e.g. other bounds that are already implied today.
See also this discussion.
I don't know if it's discussed anywhere in the book or the like.