Borrow-check error with variable not living long enough in nested lambda

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-12 08:37:02

问题


I am getting an error inside a nested lambda.

let rows = vec![
    vec![3, 6, 2, 8, 9, 0],
    vec![0, 0, 1, 4, 5, 1],
];

let pair_sums = rows.iter()
    .flat_map(|row| {
        (0 ..= row.len()).map(|i| row[i] + row[i + 1])
    })
    .collect::<Vec<_>>();

println!("{:?}", pair_sums);
error[E0597]: `row` does not live long enough
  --> src/main.rs:9:40
   |
9  |             (0..row.len() - 1).map(|i| row[i] + row[i + 1])
   |                                    --- ^^^ does not live long enough
   |                                    |
   |                                    capture occurs here
10 |         })
   |         - borrowed value only lives until here
11 |         .collect::<Vec<_>>();
   |                            - borrowed value needs to live until here

I can sort of see why this is happening, and I can fix it by threading the value of row through to the inner lambda:

let pair_sums = rows.iter()
    .flat_map(|row| { 
        (0 ..= row.len()).zip(iter::repeat(row))
            .map(|(i, row)| row[i] + row[i + 1])
    })
    .collect::<Vec<_>>();

This is horrible and can't be the best solution. How can I refer to variables in a parent scope without having to pass them along explicitly?


回答1:


The trick here is how closures capture their variables: they will take references to them if it is allowed by the contents of the closure, without looking at how they are used, to keep the inference local to the closure expression and predicable. In this case the row variable is only ever used by reference, so it is fine to be captured by reference; that is, the closure object passed to map contains a reference to row. This object hence cannot leave the scope that declares the row variable (i.e. flat_map's closure) because that reference would be left pointing to invalid memory. Returning .map(closure) will fall foul of this rule, as .map creates a lazy iterator that stores the closure and only calls it as elements are requested.

The fix here is to force the row variable to not be captured by reference, so that the closure can leave the scope. This can be done with the move keyword:

let pair_sums = rows.iter()
    .flat_map(|row| { 
        (0..row.len() - 1)
            .map(move |i| row[i] + row[i + 1])
    })
    .collect::<Vec<_>>();

In other words, the original code is equivalent to something like:

let pair_sums = rows.iter()
    .flat_map(|row: &Vec<i32>| { 
        let row_ref: &&Vec<i32> = &row;
        (0..row.len() - 1)
            .map(move |i| (*row_ref)[i] + (*row_ref)[i + 1])
    })
    .collect::<Vec<_>>();

(My Finding Closure in Rust post digs into closures in more detail, as does the Rust book.)



来源:https://stackoverflow.com/questions/33600843/borrow-check-error-with-variable-not-living-long-enough-in-nested-lambda

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!