error[E0382]: use of moved value: `cb`
--> src/main.rs:12:18
|
7 | let cb = move |s: &str| { // captures {s, captured} - {s} = {captured}
| -- move occurs because `cb` has type `[closure@src/main.rs:7:14: 10:6 captured:main::NotCopy]`, which does not implement the `Copy` trait
...
11 | do_something(cb, "hello");
| -- value moved here
12 | do_something(cb, "world"); // ERROR
| ^^ value used here after move
the other thing is that, even if something is moved into / captured by a closure, a shared reference to the closure may suffice to call it: that's what being Fn(...) -> _ means.
This is best seen with a counter example: what does a closure that can't be called from a shared reference look like? There are two possibilities here:
FnMut
the closure needs at least a unique reference to it to be callable. This is often the case when the closure body uses a unique reference (&mut _) to mutate something:
fn main()
{
let mut cb_counter = 0;
let cb = |s: &str| { // captures {s, &mut cb_counter} - {s} = {&mut cb_counter}
cb_counter += 1; // *(&mut cb_counter) += 1;
println!("result: {}", s);
};
do_something(cb, "hello"); // error, cb needs at least a unique reference to be callable
}
fn do_something<F> (f: F, label: &'_ str)
where
F : Fn(&str) -> (), // needs to be callable from a shared reference
{
f(label); // (&f)(label)
}
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
--> src/main.rs:4:14
|
4 | let cb = |s: &str| { // captures {s, &mut cb_counter} - {s} = {&mut cb_counter}
| ^^^^^^^^^ this closure implements `FnMut`, not `Fn`
5 | cb_counter += 1; // *(&mut cb_counter) += 1;
| ---------- closure is `FnMut` because it mutates the variable `cb_counter` here
...
8 | do_something(cb, "hello"); // error, cb needs at least a unique reference to be callable
| ------------ the requirement to implement `Fn` derives from here
In this case the compilation error comes from the bounds on do_something being too strict (we are not calling f from multiple places at the same time, thus we don't need the : Fn bound). Changing the F : Fn(&str) -> () bound to F : FnMut(&str) -> () (and the f: F argument into mut f: F, to be able to use &mut f) fixes this:
fn do_something<F> (mut f: F, label: &'_ str)
where
F : FnMut(&str) -> (),
{
f(label); // (&mut f)(label)
}
FnOnce
the closure needs to consume part of its environment when called; this can happen when the closure returns an owned value (that is not Copy) from its captured environment:
fn main()
{
let mut string = String::from("Hello");
// captures {s, string} - {s} = {string}
let append_and_return = |s: &str| -> String {
use ::std::fmt::Write;
write!(&mut string, ", {}!", s).unwrap();
string
};
do_something(append_and_return, "world"); // error
}
fn do_something<F> (mut f: F, label: &'_ str)
where
F : FnMut(&str) -> String,
{
let string = f(label);
dbg!(string);
}
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/main.rs:5:29
|
5 | let append_and_return = |s: &str| -> String {
| ^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
...
8 | string
| ------ closure is `FnOnce` because it moves the variable `string` out of its environment
9 | };
10 | do_something(append_and_return, "world"); // error
| ------------ the requirement to implement `FnMut` derives from here
Again, changing the strict bounds (we are not calling f multiple times, just once) to looser ones (mut f: F no longer needed by the way) fixes this:
fn do_something<F> (f: F, label: &'_ str)
where
F : FnOnce(&str) -> String,
{
let string = f(label);
dbg!(string);
}
which reads as: a Fn closure (i.e., a closure flexible enough to be callable from multiple places at once) is also a FnMut closure (i.e., a closure that can be called multiple times sequentially), and a FnMut closure is also a FnOnce closure (i.e., a closure that can be called once).
That's why, the bound on a closure, when it is one of the arguments of a function, should be as loose as possible:
FnOnce when called one or zero times;
FnMut otherwise.
requiring Fn is very rare (and thus usually wrong), since such bound is usually only needed when there may be multiple concurrent (or parallel with an added + Sync bound) calls to the closure, like the ::rayon crate does.