英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论