Rust闭包报错closure may outlive函数:原因及显式生命周期可行性
Rust闭包生命周期问题:为何内层闭包需要move关键字?
问题代码
fn process_grid(grid: Vec<Vec<i32>>) -> Vec<i32> { grid.iter() .enumerate() .flat_map(|(i, row)| { row.iter() .enumerate() // closure may outlive the current function, // but it borrows `i`, which is owned by the current function .flat_map(|(j, val)| process(i, j, *val)) }) .collect() } fn process(i: usize, j: usize, val: i32) -> Option<i32> { todo!() }
编译错误信息
error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function --> src/main.rs:291:27 | 291 | .flat_map(|(j, val)| process(i, j, *val)) | ^^^^^^^^^^ - `i` is borrowed here | | | may outlive borrowed value `i` | note: closure is returned here --> src/main.rs:289:13 | 289 | / row.iter() 290 | | .enumerate() 291 | | .flat_map(|(j, val)| process(i, j, *val)) | |_________________________________________________________^ help: to force the closure to take ownership of `i` (and any other referenced variables), use the `move` keyword | 291 | .flat_map(move |(j, val)| process(i, j, *val)) | ++++
用户疑问
添加move关键字确实能解决问题,但存在以下疑问:
- 内层闭包
|(j, val)| process(i, j, *val)怎么可能比外层闭包存活更久? - 使用显式生命周期是否能解决这个问题?
解答
为什么不加move会编译失败?
核心原因是迭代器的延迟执行特性。外层闭包|(i, row)| { ... }返回的是一个未执行的迭代器,这个迭代器并不会在外层闭包执行期间立刻遍历元素,而是要等到后续collect()调用时才会逐步执行内层闭包。
当内层闭包引用i却不加move时,它只是借用了外层闭包的局部变量i。但外层闭包执行完毕后,i就会被销毁。如果内层闭包在i销毁后才被调用,就会出现悬垂引用——Rust编译器会直接阻止这种不安全的情况发生,因此抛出错误。
内层闭包确实会比外层闭包存活更久
外层闭包的作用仅仅是生成迭代器,它执行完成后就会退出,局部变量i随之被回收。但生成的迭代器会被传递到外层flat_map中,直到整个链式调用走到collect()阶段,才会逐个触发内层闭包的执行。此时外层闭包早已执行完毕,i已经不在栈上,内层闭包如果持有i的引用,就会非法访问已释放的内存。
显式生命周期无法解决此问题
生命周期注解只能描述引用之间的存活关系,不能改变变量的所有权或延长变量的存活时间。这里的问题是i的生命周期仅限于外层闭包执行期间,而内层闭包需要在这之后继续存在。显式生命周期无法让i的存活时间覆盖内层闭包的执行周期,也无法让内层闭包安全持有已销毁变量的引用。只有通过move关键字让内层闭包获取i的所有权,才能保证i在内层闭包执行期间始终有效。
内容的提问来源于stack exchange,提问作者St.Antario
相关产品推荐
相关产品推荐

