Lifetime of a returned iterator

Hey, I wrote a function that reruns an iterator over a vector after some filtering. Since this function needs to be used in a context where I have a mutable reference on the vector (and I don't want to iterate over a copy of it), I wrote:

    pub fn iter_free_spots<'a>(&'a mut self) -> impl Iterator<Item = (usize, usize)> + 'a {
        self.v.iter()
        .enumerate()
        .filter(|e| *e.1 == Spot::Empty)
        .map(|e| {
            self.get_tuple_index(e.0)
        })
    }

it should return the indices of the free spots. as self.v is defined as Vec<Spot> and Spot is an enum.

rust (version 1.43.1) gives me:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
   --> src/board.rs:100:14
    |
100 |         .map(|e| {
    |              ^^^ may outlive borrowed value `self`
101 |             self.get_tuple_index(e.0)
    |             ---- `self` is borrowed here
    |
note: closure is returned here
   --> src/board.rs:97:9
    |
97  | /         self.v.iter()
98  | |         .enumerate()
99  | |         .filter(|e| *e.1 == Spot::Empty)
100 | |         .map(|e| {
101 | |             self.get_tuple_index(e.0)
102 | |         })
    | |__________^
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
    |
100 |         .map(move |e| {
    |              ^^^^^^^^

which is an amazing error message, but when I move e (as the help suggests) I get:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
   --> src/board.rs:100:14
    |
100 |         .map(|e| {
    |              ^^^ may outlive borrowed value `self`
101 |             self.get_tuple_index(e.0)
    |             ---- `self` is borrowed here
    |
note: closure is returned here
   --> src/board.rs:97:9
    |
97  | /         self.v.iter()
98  | |         .enumerate()
99  | |         .filter(|e| *e.1 == Spot::Empty)
100 | |         .map(|e| {
101 | |             self.get_tuple_index(e.0)
102 | |         })
    | |__________^
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
    |
100 |         .map(move |e| {
    |              ^^^^^^^^

I guess I should tell the compiler that e (should be of type (usize, Spot)) should have the same lifetime as self.
is this correct?
If so, how can this be declared?

You don't declare lifetimes. Lifetimes come from the shape of your code, so to change what the lifetimes are, you must change the shape of the code.

In this case the issue is that the mutable reference to self is moved into the closure, but the iterator over v must exist at the same time. Unfortunately, mutable references must have exclusive access, so you can't have a mutable reference in the closure together with that iterator, as the mutable reference would then not have exclusive access to the stuff in the vector.

There's also the issue that move is missing on the closure, but it wont solve the problem to add it.

Does get_tuple_index need mutable access to self?

1 Like

get_tuple_index does not, it is just a small helper method. I guess that the compiler is saying my design is flawed. returning an iterator over mutable reference does sounds like a bad idea. maybe the right solution is to consume the iterator (collect) and return the whole result.

It looks like your code would work with immutable references?

hmm, problem here is that on the calling site I have a mutable reference to the vector under discussion. To my best understanding, this means I can (edit: can't) have another immutable one.

You can always do &*my_mutable_reference to temporarily get an immutable reference to the same thing. If you could call the function with a mutable reference, you can always also call it with an immutable one.

2 Likes

Right! this makes perfect sense. Thanks a lot @alice !