3
votes

The code

pub trait Q<S> {
    fn f();
}

pub trait A {
    type I;
    type F: Q<Self::I>;
}

// this works (1)
//
// pub struct S<T>
// where
//     T: A
// {                 

// unsatisfied trait bound (2)
pub struct S<T>                                  
where
    T: A<I = bool>,
{

    t: T,
}

fails to compile:

error[E0277]: the trait bound `<T as A>::F: Q<bool>` is not satisfied
  --> src/main.rs:18:1
   |
18 | / pub struct S<T>                                  
19 | | where
20 | |     T: A<I = bool>,
21 | | {
22 | |     t: T,
23 | | }
   | |_^ the trait `Q<bool>` is not implemented for `<T as A>::F`
   |
   = help: consider adding a `where <T as A>::F: Q<bool>` bound
   = note: required by `A`

Interestingly, it works if you use the commented out line (1) instead of (2). It also works if you turn the associated type I into a generic type (write trait A<I> and A<bool>).

impl<T> S<T>
    where T: A
{
    fn g() {
        T::F::f()
    }
}

succeeds with line (1) or generic type I, so T::F : Q<bool> is indeed assumed in those cases.

Why is the trait bound automatically assumed with line (1) or generic types, but not with line (2)?

Can we fix the above code without appending where T::F: Q<bool> every time we use T: A<I=bool>?

1

1 Answers

4
votes

As of Rust 1.18, the compiler requires you to write these bounds in order for the types to be well-formed. Basically, in order for the bound T: A<I = bool> to hold, it is required that the bound T::F: Q<bool> also holds. For example, if some type tried to implement A like this:

struct Y;
struct Z;

impl A for Y {
    type I = bool;
    type F = Z; // Z does not implement Q<bool>
}

then Y would not be well-formed, because the bound T::F: Q<bool> doesn't hold (and indeed, the compiler gives an error on the impl). But frustratingly, for now, the bound T::F: Q<bool> must be given explicitly whenever the bound T: A<I = bool> appears. In some way, it reassures the compiler that, hey, T::I = bool there too!

Why is the trait bound automatically assumed with line (1) or generic types, but not with line (2)?

With line (1), the bound would be T::F: Q<T::I>, which is precisely the requirement of A (with T substituted for Self). Indeed, we can also write the bound equivalently like this:

pub trait A
where
    Self::F: Q<Self::I>,
{
    type I;
    type F;
}

With line (2), the bound T::F: Q<bool> may look like it's just a matter of replacing T::I with bool, but that difference is important to the compiler; T::I is an associated type, while bool is a concrete type.


The Rust developers are considering improving the compiler so that bounds like this don't have to have to be repeated all over the place by having the compiler infer those bounds.