Search code examples
rustlifetime

Rust Template Parameter with lifetime cannot be moved inside the trait closure


I found the following trait definition in some blog post.

trait HasIter<'a, _Dummy=&'a Self, _Item = &'a <Self as IntoIterator>::Item>: IntoIterator 
{
    type Iter: Iterator<Item = _Item>;
}

I tried playing around with it and decided to replace _type in the declaration of Iter as follows:

trait HasIter<'a, _Dummy=&'a Self>: IntoIterator 
{
    type Iter: Iterator<Item = &'a <Self as IntoIterator>::Item>;
}

As soon as I do that, the compiler complains

the associated type `<Self as IntoIterator>::Item` may not live long enough

I suppose I miss some lifetime concept in Rust. What in the end is the difference here? Since we attach the same lifetime 'a in both cases, why should we not be allowed to do this?


Solution

  • In the second example, you have &'a <Self as IntoIterator>::Item but what if <Self as IntoIterator>::Item refers to a type that contains a reference that doesn't live as long as 'a? The reference cannot be valid for longer than the data it points to. The compiler tells you how to fix this:

      = help: consider adding an explicit lifetime bound `<Self as IntoIterator>::Item: 'a`...
      = note: ...so that the reference type `&'a <Self as IntoIterator>::Item` does not outlive the data it points at
    

    Sure enough, this compiles:

    trait HasIter<'a, _Dummy=&'a Self>: IntoIterator
    where <Self as IntoIterator>::Item: 'a
    {
        type Iter: Iterator<Item = &'a <Self as IntoIterator>::Item>;
    }
    

    The reason you don't get the same error in the first example is because _Item = &'a <Self as IntoIterator>::Item specifies a generic type with the provided type as a default. The default doesn't have to make sense in all cases, because it can simply be rejected at the site where HasIter is implemented if <Self as IntoIterator>::Item: 'a isn't true. In contrast, the bound on Iter in the second example has to hold for all possible implementations of the trait.