I'm learning closures for the first time; I find it hard so these below are questions to get a sense of my understanding and hopefully expand/correct it.
First, my summary is:
Trait | Executions | Move Into | Move Out | Mutation |
---|---|---|---|---|
FnOnce | n<=1 | Allowed | Allowed | Allowed (?) |
FnMut | n >= 0 | Allowed | Not Allowed | Allowed |
Fn | n >= 0 | Allowed | Not Allowed | Not Allowed |
Source Paragraph
FnOnce
applies to closures that can be called once. All closures implement at least this trait because all closures can be called. A closure that moves captured values out of its body will only implementFnOnce
and none of the otherFn
traits, because it can only be called once.FnMut
applies to closures that don’t move captured values out of their body, but that might mutate the captured values. These closures can be called more than once.Fn
applies to closures that don’t move captured values out of their body and that don’t mutate captured values, as well as closures that capture nothing from their environment. These closures can be called more than once without mutating their environment, which is important in cases such as calling a closure multiple times concurrently.
More specifically, taking just:
FnMut
applies to closures that don’t move captured values out of their body, but that might mutate the captured values. These closures can be called more than once.
I was confused by the "move in and move out" here. And others too.
Snippets and Questions
Here are some examples I made up and were tricky for me to analyse (alongside questions to get help / guidance with):
- Example 1
fn main(){
let x = String::from("hello");
let move_x_into_closure = move || {
println!("{}", x);
};
move_x_into_closure();
move_x_into_closure();
// println!("{x}"); // fails, x was moved into the closure.
}
So in this case, the ownership of x
is moved into the closure (and out of x
). One can still run the function many times.
- Question 1: It's a bit mysterious to me where is the
x
stored? (Especially given the following example.)
This one would pass though, since the print
only takes a borrow (unless we return or use x;
):
fn main(){
let x = String::from("hello");
let x_stays_alive_outside_closure = || {
println!("{}", x);
};
x_stays_alive_outside_closure();
x_stays_alive_outside_closure();
x;
}
- Question 2: in both cases above the Trait should be
Fn
?Since it does not move ownership out (which I understand asreturn x
or justx
in the last expression) , and it does not mutate any variable (just takes an immutable borrow.)
But if one writes instead:
fn main(){
let x = String::from("hello");
let move_in_out = move || {
println!("{}", x);
x
};
move_in_out();
// move_in_out();
// x;
}
- Question 3: Now it's
FnOnce
? So uncommenting fails.