I wanted to implement the Try trait (Playground link), but I ran into a strange design decision.
The issue
The Try trait inherits from the FromResidual trait, but the FromResidual
trait uses type from the Try
trait? This line in particual:
pub trait FromResidual<R = <Self as Try>::Residual> {
seems off. One of the best features of Rust (in my opinion) is the lack of magic going on. For example, you have to re-define all the generic bounds when implementing a struct, which is tedious, but more verbose, and thus you can see which the generic bounds must the types comply with right at the implementation, with no need to go and check the struct definition.
Why I think it's wrong
This, on the other hand, is going in the opposite direction. Here, from the playground example:
impl<S, F, I> FromResidual for Outcome<S, F, I> {
can you tell me what is the R
type in FromResidual
? You would have to look at the docs to see that it is inferred from the Try
subtrait, and then try to look for the Try
implementation to find out.
Comparison to DerefMut
A similar thing is going on with DerefMut
, but there the type is inherited from a supertrait, which makes much more sence, whereas here it is almost as if it was inhered from a subtrait.
Combining generic and type-associated trait
I think part of the problem is that Try
is an associated-types traits, and FromResidual
is a generic trait, but that should be no excuse for this type of "hidden magic" going on. The Try
trait already defines that the FromResidual
generic type must be it's Residual
associated type:
pub trait Try: FromResidual<Self::Residual> {
so there is no need (IMO) to specify the the generic type in FromResidual
should be taken from the Try
subtrait at FromResidual
's definition.
Proposed solution
Generic or associated types should never be inferred from subtraits, only inhereted from supertraits. FromResidual
's definition should simply be
pub trait FromResidual<R> {
and all implmentations should have to explicitly specify the type - example from the playground link:
impl<S, F, I> FromResidual<Failure<F, I>> for Outcome<S, F, I> {