为啥通过借用 u32 可以使闭包比当前函数寿命更长?

Posted

技术标签:

【中文标题】为啥通过借用 u32 可以使闭包比当前函数寿命更长?【英文标题】:Why may a closure outlive the current function by borrowing a u32?为什么通过借用 u32 可以使闭包比当前函数寿命更长? 【发布时间】:2022-01-14 01:34:18 【问题描述】:

我来了

错误[E0373]:闭包可能比当前函数寿命更长,但它借用了当前函数拥有的row_nr

我不希望这样,因为row_nru32,所以我希望它被复制而不是移动:


fn get_neighbours(values: &Vec<Vec<u32>>, row: usize, col: usize) -> Vec<u32> 
    vec![
        values.get(row - 1).and_then(|cols| cols.get(col)),
        values.get(row).and_then(|cols| cols.get(col - 1)),
        values.get(row).and_then(|cols| cols.get(col + 1)),
        values.get(row + 1).and_then(|cols| cols.get(col)),
    ].into_iter().filter_map(|value_opt| value_opt.map(|value| *value)).collect()


fn get_points(values: Vec<Vec<u32>>) -> Vec<(u32, Vec<u32>)> 
    values
        .iter()
        .enumerate()
        .flat_map(|(row_nr, columns)| 
            columns.iter().enumerate().map(|(column_nr, height)| 
                let neighbours = get_neighbours(&values, row_nr, column_nr);
                (*height, neighbours)
            )
        ).collect()

完整的错误信息:

  --> src/lib.rs:16:44
   |
16 |             columns.iter().enumerate().map(|(column_nr, height)| 
   |                                            ^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `row_nr`
17 |                 let neighbours = get_neighbours(&values, row_nr, column_nr);
   |                                                          ------ `row_nr` is borrowed here
   |
note: closure is returned here
  --> src/lib.rs:16:13
   |
16 | /             columns.iter().enumerate().map(|(column_nr, height)| 
17 | |                 let neighbours = get_neighbours(&values, row_nr, column_nr);
18 | |                 (*height, neighbours)
19 | |             )
   | |______________^
help: to force the closure to take ownership of `row_nr` (and any other referenced variables), use the `move` keyword
   |
16 |             columns.iter().enumerate().map(move |(column_nr, height)| 
   |                                            ++++

另见on the Rust playground。

现在,错误建议使用move,但这会产生问题,因为我只想传递对values Vec 的引用(我认为它不应该比闭包更有效,因为get_neighbours 需要u32s 来自那个 Vec,因此我认为也应该制作副本?)。

我想我在这里误解了生命周期。对于我误解的内容,我将不胜感激。我查看了几个相关问题,但他们似乎要么使用move 解决它(如上所述,我认为我应该(能够)避免?),或者有多个借用检查器问题,使它我很难理解哪些适用于我(例如this answer)。

【问题讨论】:

【参考方案1】:

我没想到会这样,因为 row_nru32,所以我希望它被复制而不是移动

这个预期是正确的,假设它首先被移动了。在这种情况下,它不会被移动,而是被借用,因为默认情况下,闭包会从它们的环境中借用值。要请求move,这对于u32 确实会产生副本,您需要明确使用move 关键字。

正如您所发现的,当您只使用move 时,您也会移动values,它不会编译并且无论如何都不是您想要的。仅将一个值移动到闭包中的标准 Rust 习惯用法是使用 move 并显式借用您想要移动的捕获变量(在你的情况下为 values)。例如,这样编译:

.flat_map(|(row_nr, columns)| 
    columns.iter().enumerate().map(
        let values = &values;
        move |(column_nr, height)| 
            let neighbours = get_neighbours(values, row_nr, column_nr);
            (*height, neighbours)
        
    )
)

Playground

至于您关于为什么闭包可能比当前函数寿命更长的名义问题:请注意,错误消息中的“当前函数”是指外部闭包,即传递给flat_map() 的闭包。由于内部闭包成为map() 返回的迭代器的一部分,并且外部闭包立即返回该迭代器,因此内部闭包确实比“当前函数”寿命更长,并且不能允许借用@987654334 @ 或 columns。 (但借用values 完全没问题,因为values 比两个闭包都长。)

【讨论】:

以上是关于为啥通过借用 u32 可以使闭包比当前函数寿命更长?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 c 中的幂函数需要比预期更长的时间

为啥通过 ASP.NET 方法运行查询比原生 SQL 需要更长的时间?

JavaScript——闭包

这个实例如何看起来比它自己的参数生命周期更长?

ASP.NET 会话的寿命是不是比应用程序长

为啥偶数 N 比奇数 N 花费更长的时间?