Why is the type parameter of this blanket trait implementation not considered to be constrained?

Viewed 27

I'm trying to understand how type constraints interact with associated types, and I've hit upon this case that I don't understand. I've read the RFC on unconstrained types in generic impls, but I can't see how this fits into the list of disallowed things. Intuitively, I would expect that for any type implementing Baz<T>, this code would specify exactly one unambiguous impl of Foo. Am I wrong about that, or can the compiler just not make that leap of logic? If not, why not?

trait Foo {
    type Bar;
    fn foo(&self) -> Self::Bar;
}

trait Baz<T> {
    fn foo(&self) -> T;
}

impl<T, TBaz> Foo for TBaz
where
  TBaz: Baz<T>
{
    type Bar = T;
    fn foo(&self) -> Self::Bar {
        Baz::<T>::foo(self)
    }
}

(Playground)

error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:10:6
   |
10 | impl<T, TBaz> Foo for TBaz
   |      ^ unconstrained type parameter

For more information about this error, try `rustc --explain E0207`.
1 Answers

Intuitively, you are wrong because there is not one unambiguous impl. Assume a type SomeType that implements both Baz<i32> and Baz<f32>. What impl will we choose? What will be the value of the associated type Bar - i32 or f32?

Formally (and from the compiler point of view), you are wrong because you are breaking the rules:

Generic parameters constrain an implementation if the parameter appears at least once in one of:

  • The implemented trait, if it has one
  • The implementing type
  • As an associated type in the bounds of a type that contains another parameter that constrains the implementation

Type and const parameters must always constrain the implementation. Lifetimes must constrain the implementation if the lifetime is used in an associated type.

T appears neither in the implemented trait (Foo), nor in the implementing type (TBaz) or as an associated type. And it is a type parameter.

I think your intuition failure was by the assumption that TBaz will implement Baz only for one type, probably by a generic implementation impl<T> Baz<T> for TBaz<T>. But this is not enforced by the language. There is a mechanism that enforces that - associated types. If T was an associated type of Baz it was fine.

Related