Why does this trait bound need to be shown?

For the following code:

trait List /* supertypeclass */
{
  type Next: List;
}
trait ListExtend: List
where
  <Self as List>::Next: ListExtend
{}

when you run cargo check, it returns 148 and says

    Checking tc_inheritance v0.1.0 (/home/rust/tc_inheritance)
error[E0277]: the trait bound `<<Self as List>::Next as List>::Next: ListExtend` is not satisfied
  --> src/lib.rs:15:25
   |
15 |   <Self as List>::Next: ListExtend {}
   |                         ^^^^^^^^^^ the trait `ListExtend` is not implemented for `<<Self as List>::Next as List>::Next`
   |
note: required by a bound in `ListExtend`
  --> src/lib.rs:15:25
   |
12 | pub trait ListExtend
   |           ---------- required by a bound in this trait
...
15 |   <Self as List>::Next: ListExtend {}
   |                         ^^^^^^^^^^ required by this bound in `ListExtend`
help: consider further restricting the associated type
   |
15 |   <Self as List>::Next: ListExtend, <<Self as List>::Next as List>::Next: ListExtend {}
   |                                   ++++++++++++++++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `tc_inheritance` (lib) due to 1 previous error

rustc somehow wants to show <Self as List>::Next: ListExtend, but isn't that a premise?

possibly related:

1 Like

Well, if I modify your code to use a supertrait bound:

trait List {
    type Next: List;
}
trait ListExtend: List<Next: ListExtend> {}

then I get this error:

error[E0391]: cycle detected when computing the implied predicates of `ListExtend`
 --> src/lib.rs:4:30
  |
4 | trait ListExtend: List<Next: ListExtend> {}
  |                              ^^^^^^^^^^
  |
  = note: ...which immediately requires computing the implied predicates of `ListExtend` again
note: cycle used when computing normalized predicates of `ListExtend`
 --> src/lib.rs:4:1
  |
4 | trait ListExtend: List<Next: ListExtend> {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So, this definitely isn't due to lack of elaboration because elaboration wouldn’t work either. I’m not familiar enough with type-level-list or induction shenanigans to say whether there's a way to have the original definition accepted.

1 Like

Here's a workaround:

trait List {
    type Next: List;
}

trait ListExtend: List<Next = Self::NextExtend> {
    type NextExtend: ListExtend;
}

This looks like the issue for the E0391 and maybe also the E0277. This issue looks more E0277 specific.


I guess I'll also note that in the above workaround, it becomes a breaking change to remove the ListExtend bound from the associated type (which I'm guessing you're fine with, but FYI).

2 Likes