Incorrect lifetime error when returning opaque type from trait method

Hi, I've noticed what seems to be an error in rustc. Here's a minimal code example:

trait Foo {
    fn iter<'a>(&self, v: &'a Vec<usize>) -> impl Iterator<Item = usize> + 'a;
}

struct Bar;

impl Foo for Bar {
    fn iter<'a>(&self, v: &'a Vec<usize>) -> impl Iterator<Item = usize> + 'a {
        v.iter().copied()
    }
}

Basically, I have a trait Foo which has a method iter that produces an iterator whose lifetime is bound to the argument v, but not to self. I implement this on a struct Bar. However, when I end up using that method, e.g.:

impl Bar {
    fn skip_first<'a>(&self, v: &'a Vec<usize>) -> impl Iterator<Item = usize> + 'a {
        self.iter(v).skip(1)
    }
}

I get a lifetime error, saying impl Iterator<Item = usize> + 'a implicitly captures &self's lifetime:

error[E0700]: hidden type for `impl Iterator<Item = usize> + 'a` captures lifetime that does not appear in bounds
   --> varbade/src/lib.rs:144:13
    |
143 |         fn skip_first<'a>(&self, v: &'a Vec<usize>) -> impl Iterator<Item = usize> + 'a {
    |                           -----                        -------------------------------- opaque type defined here
    |                           |
    |                           hidden type `Skip<impl Iterator<Item = usize> + 'a>` captures the anonymous lifetime defined here
144 |             self.iter(v).skip(1)
    |             ^^^^^^^^^^^^^^^^^^^^
    |
help: to declare that `impl Iterator<Item = usize> + 'a` captures `'_`, you can add an explicit `'_` lifetime bound
    |
143 |         fn skip_first<'a>(&self, v: &'a Vec<usize>) -> impl Iterator<Item = usize> + 'a + '_ {
    |                                                                                         ++++

This is not correct, and indeed if I move the implementation of fn iter outside of the trait and into a regular impl Bar { ... }, the error disappears.

Is this a limitation of opaque return types in traits? Thanks for your help :slight_smile:

Until we get precise capturing, -> impl Trait in traits captures all generic inputs, including lifetimes (such as the lifetime on &self). In the next edition, -> impl Trait outside of traits will act like this too.

(Note that + 'a is not the same thing as precise capturing, i.e. it doesn't mean "capture 'a and no other generic lifetime inputs". Instead it means "impose a : 'a lifetime bound on the return type". The other lifetimes and other generics are still captured. There are more details in the linked RFCs.)

In the meanwhile you can use GATs and/or type erasure (Box<dyn Iterator<Item = usize> + 'a>) if that's acceptable for your use case.

1 Like

Thanks for the explanation! I'll try some of the alternatives, see what fits :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.