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?
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.