rust-lang / rust-lang/rust

Type equality bounds disable implied projection outlives bounds

Open
#141,054 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-implied-bounds A-lifetimes A-trait-system C-bug T-types
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).

I tried this code:

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.