为什么Rust无法识别我在闭包内重新分配移动的变量?

时间:2017-12-07 01:47:50

标签: rust borrow-checker

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

当我移动一个变量然后在一个闭包之外重新分配它时,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应该满足借用检查器,移动后不会访问任何变量。我错过了什么吗?

2 个答案:

答案 0 :(得分:3)

正如@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中完全可以接受,因为您永远不会再次调用该闭包,但在下次调用时FnMutFn中不能接受... <你会对v 做什么?

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

答案 1 :(得分:2)

  

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

注意错误消息详细信息 - 没有开始的步骤:

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

由于没有搬出,将东西搬回来是没有意义的。

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

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);
    });
}