Implied lifetime bound of associated types

I just hit problem with satisfying the lifetime bound of a generic type parameter that's fed into the GAT of the trait it is implementing. When I write impl <T, CST> CoordsIter for CST where CST: CoordSeqTrait<T = T>, there seems to be no lifetime bound applied to T, but when I instead use CST::T inside the impl, T seems to have a lifetime bound. Here is an example with code that does not compile and a variant that compiles (Playground):

#[derive(Copy, Clone)]
struct Coord<T> {
    x: T,
    y: T,
}

trait CoordsIter {
    type Iter<'a>: Iterator<Item = Coord<Self::Scalar>>
    where
        Self: 'a;

    type Scalar;

    fn coords_iter(&self) -> Self::Iter<'_>;
}

// Same as CoordsIter, but with a different name.
trait CoordsIter2 {
    type Iter<'a>: Iterator<Item = Coord<Self::Scalar>>
    where
        Self: 'a;

    type Scalar;

    fn coords_iter(&self) -> Self::Iter<'_>;
}

trait CoordSeqTrait {
    type T;
}

// This does not compile.
impl<T, CST> CoordsIter for CST
where
    CST: CoordSeqTrait<T = T>,
{
    type Iter<'a> = Box<dyn Iterator<Item = Coord<T>> + 'a>
    where
        Self: 'a;

    type Scalar = T;

    // error[E0311]: the parameter type `T` may not live long enough
    //   --> src/coords_iter.rs:65:9
    //   |
    // 61 |     fn coords_iter(&self) -> Self::Iter<'_> {
    //   |                    ----- the parameter type `T` must be valid for the anonymous lifetime defined here...
    // ...
    // 65 |         Box::new(iter)
    //   |         ^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    //   |
    // help: consider adding an explicit lifetime bound
    //   |
    // 61 -     fn coords_iter(&self) -> Self::Iter<'_> {
    // 61 +     fn coords_iter<'a>(&'a self) -> Self::Iter<'a> where T: 'a {
    //   |
    fn coords_iter(&self) -> Self::Iter<'_> {
        let all_coords: Vec<Coord<T>> = vec![];
        let iter = all_coords.into_iter();
        Box::new(iter)
    }
}

// This compiles.
impl<CST> CoordsIter2 for CST
where
    CST: CoordSeqTrait,
{
    type Iter<'a>
        = Box<dyn Iterator<Item = Coord<CST::T>> + 'a>
    where
        Self: 'a;

    type Scalar = CST::T;

    fn coords_iter(&self) -> Self::Iter<'_> {
        let all_coords: Vec<Coord<CST::T>> = vec![];
        let iter = all_coords.into_iter();
        Box::new(iter)
    }
}

It seems to me that type T in CST::T has some implicit lifetime bound so it is not fully equivalent to defining a generic type parameter T and bound it using CST: CoordSeqTrait<T = T>, but I cannot find any reference to this implicit rule. I'd like to get explanations why these 2 impls are not equivalent, and relevant rules for lifetimes of associated types.

The rule for associated type projections (like CST::T) is from RFC 1214.

This last rule, however, is not only new, it is the crucial insight of this RFC. It states that if all the components in a projection’s trait reference outlive 'a, then the projection must outlive 'a

In this case, CoordSeqTrait has no parameters, and you know CST (aka Self) outlives the lifetime of the self: &Self reference (or the method could not be called), so CST::T must also outlive that lifetime.

I agree it's unintuitive and I can't explain why associated type equality can't be used to draw the analogous conclusion for a generic parameter. I've seen some attempts at an explanation for related issues, but have yet to fully absorb them.[1]


  1. so I can't even be sure that comment is directly applicable ↩︎

1 Like

I decided this was sufficiently different from the trait bound case to create an issue.