Type equality bounds disable implied projection outlives bounds
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
A heads-up: this is related to #109845 / #137185, but involves outlives bounds, not trait bounds. There are less workarounds for this case as it's not always possible to add an appropriate outlives bound to the implementation (while keeping the type equality bound).
trait CoordSeqTrait {
type T;
}
trait CoordsIter {
type Iter<'a>: Iterator<Item = Self::Scalar> where Self: 'a;
type Scalar;
fn coords_iter(&self) -> Self::Iter<'_>;
}
impl<T, CST: CoordSeqTrait<T = T>> CoordsIter for CST {
type Iter<'a> = Box<dyn Iterator<Item = T> + 'a> where Self: 'a;
type Scalar = T;
fn coords_iter(&self) -> Self::Iter<'_> {
Box::new([].into_iter())
}
}
I expected to see this happen: compiles, as this does:
struct Other<T>(T);
impl<CST: CoordSeqTrait> CoordsIter for Other<CST> {
type Iter<'a> = Box<dyn Iterator<Item = CST::T> + 'a> where Self: 'a;
type Scalar = CST::T;
fn coords_iter(&self) -> Self::Iter<'_> {
Box::new([].into_iter())
}
}
Instead, this happened:
error[E0311]: the parameter type `T` may not live long enough
--> src/lib.rs:15:9
|
14 | fn coords_iter(&self) -> Self::Iter<'_> {
| ----- the parameter type `T` must be valid for the anonymous lifetime defined here...
15 | Box::new([].into_iter())
| ^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
14 - fn coords_iter(&self) -> Self::Iter<'_> {
14 + fn coords_iter<'a>(&'a self) -> Self::Iter<'a> where T: 'a {
The error persists even if all occurrences of T outside of the impl header are replaced with projections.
impl<T, CST: CoordSeqTrait<T = T>> CoordsIter for CST {
- type Iter<'a> = Box<dyn Iterator<Item = T> + 'a> where Self: 'a;
- type Scalar = T;
+ type Iter<'a> = Box<dyn Iterator<Item = CST::T> + 'a> where Self: 'a;
+ type Scalar = CST::T;
The suggestion to add a lifetime bound is only applicable if you can modify the trait. If you can modify the trait, it is possible to add a bound to the example:
trait CoordsIter {
type Iter<'a>: Iterator<Item = Self::Scalar> where Self: 'a;
type Scalar;
- fn coords_iter(&self) -> Self::Iter<'_>;
+ fn coords_iter<'a>(&'a self) -> Self::Iter<'a> where Self::Scalar: 'a;
}
The version with a type equality bound can now be made to compile. However, it may be a breaking change. For the example, all preexisting implementations will break.
error[E0195]: lifetime parameters or bounds on method `coords_iter` do not match the trait declaration
--> src/lib.rs:23:19
|
8 | fn coords_iter<'a>(&'a self) -> Self::Iter<'a> where Self::Scalar: 'a;
| ---- -- this bound might be missing in the impl
| |
| lifetimes in impl do not match this method in trait
...
23 | fn coords_iter(&self) -> Self::Iter<'_> {
| ^ lifetimes do not match method in trait
Note that the above error is not about meeting the Self::Scalar: 'a bound. For this example, that's a trivial bound. The error is that the lifetime parameter went from late bound to early bound.
I guess this could be considered a feature request and not a bug, but it is quite unintuitive that the mere presence of a type equality bound triggers the lifetime errors. This issue was inspired by an URLO topic.
Meta
Playground
- Stable version: 1.87.0
- Beta version: 1.88.0-beta.3 (2025-05-11 5dadfd5c417f0b66816c)
- Nightly version: 1.89.0-nightly (2025-05-13 414482f6a0d4e7290f61)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the stable Rust 1.87.0 examples in the linked Playground, comparing the type-equality-bound implementation with the projection-based version. Investigate the related issues #109845 and #137185 and the implied outlives behavior. Done means the equality-bound example no longer produces the erroneous lifetime error without requiring a breaking trait change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100