rust 捕获的变量无法转义`FnMut`闭包体

4dc9hkyq  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(106)

我想在一个结构体中可变地覆盖两个元素,但我得到了一个错误。下面是简化的代码:

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>, //A complex structure in real code
    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){ // mutably iterate 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,但在真实的代码中存在一些问题。

63lcw9qa

63lcw9qa1#

函数fn iter_data(d: &mut Data) -> impl Iterator<Item = (usize, &mut Attr)>不能以这种方式工作,除了Rust中的实际实现可以防止这个问题之外,考虑inner: Vec<usize>包含值vec![0, 0]的情况,我会调用

let bar = iter_data(&mut foo).collect::<Vec<(usize, &mut Attr)>>();

字符串
iter_data()目前的编写方式是bar现在必须包含两个&mut--对同一个Attrvec![(0, &mut Attr), (0, &mut Attr)])的引用,这是不可能的,编译器也无法静态地证明这不会发生。
有两种方法可以解决这个问题:

  • 使用内部可变性,即RefCell可可变地借用data。这允许闭包不可变地捕获data,将FnMut降级为Fn。这也会导致在运行时将上面的精心设计的示例转换为panic!(),而Iterator实际上是collect()。或者通过任何其它方式,第二个&mut到同一个Attr即将出现。
  • 使用内部迭代,也就是重新设计iter_data(),使其接受一个闭包,该闭包对每个项都执行。这允许您根本不返回对调用方的引用,这就减轻了编译器证明这些引用不会转义的需要。

我推荐第二种方法,作为一个一般的经验法则:在处理&mut引用时,通常更容易让代码到数据,而不是让数据到代码。

相关问题