【问题标题】:Why does Rust not recognize that I re-assign to the moved variable inside a closure?为什么 Rust 无法识别我重新分配给闭包内的移动变量?
【发布时间】:2017-12-07 01:47:50
【问题描述】:

我无法理解为什么特定模式无法编译。

Rust 会识别我何时移动一个变量,然后在闭包之外重新分配给它,我认为正确地允许代码编译,但是当我尝试在将多次运行的闭包中执行相同操作时,它不会.

fn main() {
    let mut v = vec![1, 2, 3, 4];
    v.into_iter().fold(0, |a, b| a + b);
    v = vec![1, 2, 3, 4];
    vec![1, 2, 3].into_iter().for_each(|x| {
        v.into_iter().fold(x, |a, b| a + b);
        v = vec![1, 2, 3, 4];
    });
}
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
 --> src/main.rs:6:9
  |
2 |     let mut v = vec![1, 2, 3, 4];
  |         ----- captured outer variable
...
6 |         v.into_iter().fold(x, |a, b| a + b);
  |         ^ cannot move out of captured outer variable in an `FnMut` closure

在我看来,对v 的重新分配应该满足借用检查器的要求,即在移动后不会访问任何变量。我错过了什么吗?

【问题讨论】:

  • 我的猜测:FnMut 闭包可能会被多次调用。在这种情况下,for_each 将按顺序执行,但如果该闭包被传递给多个线程怎么办?您可以使用v,然后其他线程尝试访问它,然后您才能重新分配给它。但这只是一个疯狂的猜测——我自己是 Rust 新手,所以我不确定。
  • @julioolvr 我也有这个想法,但我相信 rust 具有必须实现的特性。 Send 和 Sync 特征告诉 rust 类型可以在线程之间共享,而 for_each 不需要这些类型中的任何一种,因此这永远不可能。

标签: rust borrow-checker


【解决方案1】:

正如@Shepmaster 提到的,解决方法是使用std::mem::replace。

那么,有什么区别:

    v.into_iter().fold(x, |a, b| a + b);
    v = vec![1, 2, 3, 4];

和:

    let v_old = std::mem::replace(&mut v, vec![1, 2, 3, 4]);
    v_old.into_iter().fold(x, |a, b| a + b);

?

简而言之:异常安全。

如果由于某种原因,表达式 v.into_iter().fold(...) 会出现恐慌,它会将 v 移出,并且永远不会执行下一条语句。

这在FnOnce 中是完全可以接受的,因为你永远不会再次调用闭包,但在FnMut 或Fn 中是不可接受的,就像下一次调用一样...... 你会怎么做v?

另一方面,使用std::mem::replace,您交换首先和然后执行潜在的恐慌操作。如果操作确实恐慌,那么剩下的所有“移出”都是一个临时变量,无论如何它都会在堆栈帧的末尾消失。没问题。

【讨论】:

    【解决方案2】:

    对v 的重新分配应该满足借用检查器,即在移动后不会访问任何变量

    注意错误信息的详细信息——一开始就没有动作:

    cannot move out of captured outer variable in an `FnMut` closure
    

    因为没有搬出,所以搬回来是没有意义的。

    相反,您可以通过可变引用替换值并使用旧值:

    fn main() {
        let mut v = vec![1, 2, 3, 4];
    
        vec![1, 2, 3].into_iter().for_each(move |x| {
            let v_old = std::mem::replace(&mut v, vec![1, 2, 3, 4]);
            v_old.into_iter().fold(x, |a, b| a + b);
        });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多