Rust报错:捕获变量无法逃逸FnMut闭包体,如何解决?
问题描述
我想可变迭代结构体中的两个元素,但出现了错误。以下是简化后的代码:
use std::collections::HashMap; struct Attr { value1: usize, value2: Vec<usize> } impl Default for Attr { fn default() -> Self { Self { value1: 100, value2: vec![1;1000], } } } struct Data { inner: Vec<usize>, // 实际代码中是复杂结构 attrs: HashMap<usize, Attr>, } fn iter_data(d: &mut Data) -> impl Iterator<Item = (usize, &mut Attr)> { d.inner.iter().map(|x| (*x, d.attrs.get_mut(x).unwrap())) } fn main() { let mut data = Data { inner: Vec::new(), attrs: HashMap::new() }; for i in 1..100 { data.inner.push(i); data.attrs.insert(i, Default::default()); } for (k, v) in iter_data(&mut data){ // 可变迭代data } }
报错信息:
error: captured variable cannot escape `FnMut` closure body --> src/main.rs:25:28 | 24 | fn iter_data(d: &mut Data) -> impl Iterator<Item = (usize, &mut Attr)> { | - variable defined here 25 | d.inner.iter().map(|x| (*x, d.attrs.get_mut(x).unwrap())) | - ^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | | | | | variable captured here | | returns a reference to a captured variable which escapes the closure body | inferred to be a `FnMut` closure | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape
我想了解如何避免该错误?我只是想迭代Data中的每个元素并修改对应元素的attrs。我知道可以直接以(key, value)形式迭代attrs,但实际代码存在一些问题。
解决方案
错误根源
Rust编译器禁止FnMut闭包返回指向捕获变量的引用并让其逃逸到闭包外部。这里map闭包捕获了可变引用d,返回的&mut Attr来自d.attrs.get_mut(x),这个引用会随着迭代器元素被传到闭包外。由于FnMut闭包可被多次调用,编译器无法保证这些引用不会出现重叠可变借用的风险,因此报错。
方法一:提前复制键列表,避免闭包同时持有多重引用
先把inner里的键复制一份,再迭代这个副本去获取attrs的可变引用,这样编译器能确认不会同时借用inner和attrs:
fn iter_data(d: &mut Data) -> impl Iterator<Item = (usize, &mut Attr)> { // 提前复制inner中的所有键 let keys: Vec<_> = d.inner.clone(); keys.into_iter().map(move |key| { (key, d.attrs.get_mut(&key).unwrap()) }) }
方法二:用from_fn手动构建迭代器
如果不想复制键列表,可以用std::iter::from_fn手动实现迭代逻辑,让编译器能清晰追踪引用的生命周期:
fn iter_data(d: &mut Data) -> impl Iterator<Item = (usize, &mut Attr)> { let mut inner_iter = d.inner.iter(); std::iter::from_fn(move || { inner_iter.next().map(|&key| { (key, d.attrs.get_mut(&key).unwrap()) }) }) }
这种方式下,迭代器每次只会生成一个&mut Attr,编译器能保证引用不会重叠,因为上一个引用在取下一个元素前已经不再被使用。
方法三:直接在循环中处理(最直观)
如果iter_data函数不是必须的,也可以直接在main的循环里完成逻辑,完全绕闭包捕获问题:
fn main() { // ... 初始化代码不变 for &key in &data.inner { if let Some(attr) = data.attrs.get_mut(&key) { // 在这里修改attr } } }
内容的提问来源于stack exchange,提问作者Moold
相关产品推荐
相关产品推荐

