Skip to content

Type equality bounds disable implied projection outlives bounds #141054

Open
@QuineDot

Description

@QuineDot

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 5dadfd5)
  • Nightly version: 1.89.0-nightly (2025-05-13 414482f)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-implied-boundsArea: Implied bounds / inferred outlives-boundsA-lifetimesArea: Lifetimes / regionsA-trait-systemArea: Trait systemC-bugCategory: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions