In the code below, I explicitly force the name
from the main
function to be moved into the closure, and everything works just fine:
fn main() {
let name = String::from("Alice");
let welcome = || {
let mut name = name;
name += " and Bob";
println!("Welcome, {}", name);
};
welcome();
}
I would have thought that adding a move
to the beginning of the closure would accomplish the same thing, and result in the value being moved and the creation of a FnOnce
:
fn main() {
let name = String::from("Alice");
let welcome = move || {
name += " and Bob";
println!("Welcome, {}", name);
};
welcome();
}
Instead, however, I get the error message:
error[E0596]: cannot borrow immutable local variable `welcome` as mutable
--> main.rs:9:5
|
4 | let welcome = move || {
| ------- help: make this binding mutable: `mut welcome`
...
9 | welcome();
| ^^^^^^^ cannot borrow mutably
error[E0596]: cannot borrow captured outer variable in an `FnMut` closure as mutable
--> main.rs:5:9
|
5 | name += " and Bob";
| ^^^^
What's the correct way to think about move
on a closure in this case?
I would have thought that adding a
move
to the beginning of the closure would accomplish the same thing, …
It kind of does the same thing. You just forgot to declare name
and welcome
as mutable. This code works fine:
fn main() {
let mut name = String::from("Alice");
let mut welcome = move || {
name += " and Bob";
println!("Welcome, {}", name);
};
welcome();
}
Both versions of the closure result in name
being moved into the closure. In the first version, this is implicitly caused by consuming name
inside the closure. The second version does not consume name
, but uses the move
keyword to force the move.
… and result in the value being moved and the creation of a
FnOnce
.
Moving a value into a closure does not make it FnOnce
. If a closure consumes a captured value, it becomes FnOnce
, since it obviously can do this only once. Thus, the first version of the closure is FnOnce
, since it consumes name
. The clousre above is FnMut
, and can be called multiple times. Calling it twice results in the output
Welcome, Alice and Bob
Welcome, Alice and Bob and Bob
(I used the function trait names somewhat sloppily above. In fact, every closure implements FnOnce
, since every closure can be called at least once. Some closures can be called multiple times, so they are FnMut
in addition. And some closures that can be called multiple times don't alter their captured state, so they are Fn
in addition to the other two traits.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With