在代码中,变量 x 是复制还是作为闭包中的可变引用取得的?

huangapple go评论72阅读模式
英文:

Is x in the code copied or taken as a mutable ref in the closure?

问题

我基本上在尝试理解为什么 add_to_x 具有 FnOnce 特性。

let mut x = 5;
let mut add_to_x = |n: i32| {
    x += n;
    x
};
let m = add_to_x(5);
println!("x: {}", x);
println!("m: {}", m);

另外,如果我将 add_to_x 赋值给另一个变量,闭包会被复制还是移动?

英文:

I am basically trying to understand why add_to_x has a trait of FnOnce.

let mut x = 5;
let mut add_to_x = |n: i32| {
    x += n;
    x
};
let m = add_to_x(5);
println!("x: {}", x);
println!("m: {}", m);

Also, if I assign add_to_x to another variable, would the closure be copied or moved?

答案1

得分: 0

默认情况下,在闭包中,Rust 通过引用传递值。闭包中的 x 的行为类似于 &mut x,只是它的类型仍然是 i32(编译器在你使用它时会自动解引用它)。

特别是,对于任何 T&mut T: !Clone,因此 add_to_x 不可克隆(特别是它不能被复制)。通过示例很容易证明这一点:

fn main() {
    let mut x = 5;
    let mut add_to_x = |n: i32| {
        x += n;
        x
    };
    let mut add_to_x_bis = add_to_x;
    let m = add_to_x(5);
    let _ = add_to_x_bis(5);
    println!("x: {}", x);
    println!("m: {}", m);
}

(查看 playground)。

这将失败,并显示以下错误消息,非常明确:

error[E0382]: borrow of moved value: `add_to_x`
 --> src/main.rs:8:13
  |
7 |     let mut add_to_x_bis = add_to_x;
  |                            -------- value moved here
8 |     let m = add_to_x(5);
  |             ^^^^^^^^ value borrowed here after move
  |
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x` out of its environment

想象一下,如果可以同时拥有两个有效的 add_to_x 实例会发生什么。它们都将持有对 x 的独占引用,这是禁止的。

因此,回答你的问题,闭包是移动的。

此外,add_to_x 是一个 FnMut,比 FnOnce 更强。FnOnce 意味着闭包只能执行一次。这是此特质提供的唯一保证。FnMut 意味着闭包可以执行任意次数,但调用方法接受一个对 self 的可变引用,也就是说,你需要能够产生闭包的可变引用才能调用它(因此 add_to_x 必须是可变变量)。

在这种情况下,add_to_x 修改了一个独占借用背后的东西,但可以多次调用,因此它是一个 FnMut 闭包。

英文:

By default, in closures, Rust passes values by reference. x in the closure behaves as a &mut x, except its type is still i32 (it is dereferenced automatically by the compiler whenever you use it).

In particular, since &mut T: !Clone for any T, add_to_x is not clonable (in particular, it cannot be copied). It's easy to show this in an example:

fn main() {
    let mut x = 5;
    let mut add_to_x = |n: i32| {
        x += n;
        x
    };
    let mut add_to_x_bis = add_to_x;
    let m = add_to_x(5);
    let _ = add_to_x_bis(5);
    println!("x: {}", x);
    println!("m: {}", m);
}

(see the playground).

This will fail with the following error message, which is pretty clear

error[E0382]: borrow of moved value: `add_to_x`
 --> src/main.rs:8:13
  |
7 |     let mut add_to_x_bis = add_to_x;
  |                            -------- value moved here
8 |     let m = add_to_x(5);
  |             ^^^^^^^^ value borrowed here after move
  |
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x` out of its environment

Think about what would happen if it was possible to have two instances of add_to_x valid at the same time. The two of them would be holding an exclusive reference to x, which is forbidden.

Hence, to answer your question, the closure is moved.

Besides, add_to_x is an FnMut, which is stronger than FnOnce. FnOnce means that the closure can be executed once. It's the only guarantee that this trait provides. FnMut means that the closure can be executed any number of times, but the call method takes a mutable reference to self, that is to say, you need to be able to produce a mutable reference to the closure to call it (hence the requirement of add_to_x to be a mutable variable).

In this case, add_to_x mutates something that is behind an exclusive borrow, but it can be called several times, so it's an FnMut closure.

huangapple
  • 本文由 发表于 2023年6月22日 14:23:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76529073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定