Alrighty I think it's starting to click... is the following accurate?
move is evaluated after macros.
Therefore, if the only "owned" values in a closure are those passed to macros like format!
or println!
, then because those macros re-write the code to only take references - the closure will not need to actually move anything and therefore it remains Fn/FnMut
.
If, however, a closure with move
truly consumes owned values, then it is FnOnce
?
For example, this is fine (i.e. format_cl
retains the Fn
trait)
let a = String::from("a");
let format_cl = move || {
format!("before: {}, after: {}", a, a.to_uppercase())
};
let b = format_cl();
let c = format_cl();
While this is not (i.e. format_cl
becomes FnOnce
only):
let a = String::from("a");
let format_cl = move || {
let foo = a; //<-- genuinely forces `a` to be moved in here
format!("before: {}, after: {}", foo, foo.to_uppercase())
};
let b = format_cl();
let c = format_cl();
The reason is that conceptually this:
format!("before: {}, after: {}", a, a.to_uppercase())
is really this (the actual macro-rewriting is more complicated, but it's the idea):
do_stuff("before: {}, after: {}", &a, &a.to_uppercase())
And so the first example never really uses a
as an owned value - only borrows it
Wheras in the latter example a
is actually moved, and so the closure can only be called once