【问题标题】:Why am I able to call a closure twice even though I have moved a variable into it?为什么即使我已将变量移入闭包,我仍能调用闭包两次?
【发布时间】:2019-08-13 02:09:59
【问题描述】:
fn main() {
    let mut a = String::from("dd");
    let mut x = move || {
        a.push_str("string: &str");
    };
    x();
    x();
}

我在此处添加了move 以捕获a,但我仍然可以调用x 闭包两次。 a 仍然在这里作为可变引用借用吗?为什么move 不强制移动?

【问题讨论】:

标签: rust closures move-semantics


【解决方案1】:

变量a 确实被移到了闭包中:

fn main() {
    let mut a = String::from("dd");
    let mut x = move || {
        a.push_str("string: &str");
    };
    x();
    x();

    a.len();
}
error[E0382]: borrow of moved value: `a`
 --> src/main.rs:9:5
  |
2 |     let mut a = String::from("dd");
  |         ----- move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
3 |     let mut x = move || {
  |                 ------- value moved into closure here
4 |         a.push_str("string: &str");
  |         - variable moved due to use in closure
...
9 |     a.len();
  |     ^ value borrowed here after move

不清楚为什么您认为 closure x 在调用它后会变得无效,但事实并非如此。仅适用于结构:

struct ClosureLike {
    a: String,
}

impl ClosureLike {
    fn call(&mut self) {
        self.a.push_str("string: &str");
    }
}

fn main() {
    let a = String::from("dd");
    let mut x = ClosureLike { a };
    x.call();
    x.call();
}

【讨论】:

  • a 已移至闭包。那么a 不应该在关闭调用结束时被丢弃吗?这不就是FnOnce只能调用一次的原因吗?一个等效的 move ClosureLike 实现应该有 `fn call(mut self) {` 来移动 selfnot 借用。
  • @raj 不,当关闭关闭时它将被丢弃。这里的闭包实现了FnMut;它可以被多次调用。见when does a closure implement Fn, FnMut, and FnOnce?
  • @raj 保持其余语义相同,这里是 FnOnce version of your code 。错误是不言自明的。
【解决方案2】:

这个问题来自我对闭包的错误理解。 Rust 书中记录它的方式也造成了混乱(我并不是说这本书不好)。如果其他人有同样的困惑,这就是我发现的。

闭包不只是存储作用域并在调用时运行它。它以首选方式捕获环境。包含a 的环境存储在闭包中。如何从环境中获取值决定了 trait。

a 的值会一直持续到闭包存在,除非某些操作会移动它,例如闭包返回 a 或某个方法消耗了 a。在这里,没有任何东西将 a 移出闭包,因此可以根据需要多次调用闭包。

可以从FnOnceFnMutFn 特征中获得更好的理解。这些特征取决于闭包如何捕获变量,而不是变量如何移动到闭包中。 FnMut 可以在值为 moved 的闭包上实现。

【讨论】:

    猜你喜欢
    • 2020-05-04
    • 2019-06-16
    • 2014-04-12
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多