Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why nested iterator closures won't copy values from outer scope

I'm trying to use nested iterators, where the inner iterator uses value from the outer iterator.

vec![0;10].iter().flat_map(|&a| {
    (0..10).map(|b|{
        a + b
    })
});

error: a does not live long enough

    (0..10).map(|b|{
                ^^^

note: reference must be valid for the method call...

This compiles if I move the inner closure (move |b|{), but I don't understand why it is necessary, given that a is an integer and could have been copied instead of moved.

like image 960
Kornel Avatar asked Jul 24 '16 10:07

Kornel


1 Answers

Both flat_map and map are lazy. The inner map does not use a immediately but tries to “save” it for when it will be needed later thus borrows a. But since a is local to the outer closure and you return map's result, that borrow would become invalid. You would need to consume the inner iterator:

vec![0;10].iter().flat_map(|&a| {
    (0..10).map(|b|{
        a + b
    }).collect::<Vec<_>>()
});

Of course that's not efficient, and it would be much better for the inner closure to "keep" a. You would do this by marking the inner closure as move:

vec![0;10].iter().flat_map(|&a| {
    (0..10).map(move |b|{
        a + b
    })
});

Normally, the compiler would not let you do this, because the flat_map closure does not own a, it merely has a reference to it. However, since the numeric types in Rust (like isize) implement the Copy trait, the compiler will copy a instead of trying to move it, giving the behavior you want. Note that this is also the reason why you are allowed to dereference a (using |&a|) in the flat_map; normally that would have required owning a, not merely a reference to it (which is what .iter() yields).

like image 160
mcarton Avatar answered Sep 28 '22 12:09

mcarton