Seemingly redundant bound on Self of trait impl makes Rust forget the identity of an associated type?

Consider this (toy) code:

trait A<D> {
    type B;
    fn frob(&mut self) -> Self::B where Self: A<D>;
}

enum E<D> {
    See(usize),
    Dee(D),
}

impl<D> A<D> for Vec<D> {
    type B = E<D>;
    fn frob(&mut self) -> Self::B where Self: A<D> {
        if self.len() > 3 {
            E::See(self.len()) // replacing with Self::B gives E0599 instead
        } else {
            E::Dee(self.pop().unwrap())
        }
    }
}

(Playground)

In the implementation of frob for Vec<D>, Self::B should be E<D>, so this code should compile. However, it produces the following errors, as if the compiler forgot the identity of Self::B:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:15:13
   |
13 |     fn frob(&mut self) -> Self::B where Self: A<D> {
   |                           ------- expected `<Vec<D> as A<D>>::B` because of return type
14 |         if self.len() > 3 {
15 |             E::See(self.len())
   |             ^^^^^^^^^^^^^^^^^^ expected associated type, found enum `E`
   |
   = note: expected associated type `<Vec<D> as A<D>>::B`
                         found enum `E<_>`
   = help: consider constraining the associated type `<Vec<D> as A<D>>::B` to `E<_>` or calling a method that returns `<Vec<D> as A<D>>::B`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

error[E0308]: mismatched types
  --> src/lib.rs:17:13
   |
12 |     type B = E<D>;
   |     -------------- expected this associated type
13 |     fn frob(&mut self) -> Self::B where Self: A<D> {
   |                           ------- expected `<Vec<D> as A<D>>::B` because of return type
...
17 |             E::Dee(self.pop().unwrap())
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found enum `E`
   |
   = note: expected associated type `<Vec<D> as A<D>>::B`
                         found enum `E<D>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors

Removing the seemingly redundant type bound Self: A<D> on the impl, however, makes the code compile.

What is happening here? Is this behavior intended?

Looks like yet another case of Two impls: "Expected (type parameter), got (struct that also has impl)" · Issue #24066 · rust-lang/rust · GitHub.

3 Likes

I consider it a bug in this case; either the self-referential bound should be ignored or an error. Instead, I'm guessing, it can't normalize the associated type within the trait being defined itself.

Adding where clauses guides inference, and inference prefers where clauses to the extent that this can cause problems. However, it's sometimes necessary as well, and from what I understand it's tricky to adjust without introducing breakage.

Some more possibly related issues:

2 Likes