struct Test {
cb: Box<dyn FnMut()>,
}
impl Test {
fn new(cb: Box<dyn FnMut()>) -> Test {
Test {
cb,
}
}
fn call(&mut self) {
(self.cb)();
}
}
fn print_st(st: String) {
println!("st is: {}", st);
}
fn main() {
let st = String::from("Hello");
let mut test = Test::new(Box::new(move || {
print_st(st);
}));
test.call();
}
and it hits build error:
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> test.rs:24:18
|
22 | let st = String::from("Hello");
| -- captured outer variable
23 | let mut test = Test::new(Box::new(move || {
24 | print_st(st);
| ^^ cannot move out of captured outer variable in an `FnMut` closure
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
So why I can't move the variable which has been moved to the closure to another function?
Hm... If I change it to FnOnce, the same error happens but in a different place:
error[E0507]: cannot move out of borrowed content
--> test.rs:13:10
|
13 | (self.cb)();
| ^^^^ cannot move out of borrowed content
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
It looks like the error message says that I'm trying to move the "self" out but self is a reference.
I just want to call the "cb" why Rust wants to move it out?
Fn-like, which doesn't do anything at all with their environment, aside from possibly looking at it, and so can be called wherever you want and as many time as you want. The only restriction is that nothing changes their environment while they can still be called.
FnMut-like, which can modify their environment. Such functions can be called many times, but these calls must not overlap, and, obviously, no one else can even look on their environment while they're waiting for call.
FnOnce-like, which consume their environment and so can be called at most once. After the call, this function will be destroyed, since its environment is gone anyway.
So, if you need the closure to stay in place after the call, you can't use FnOnce.
If you want it to go away, but don't want to consume the object holding it, you probably can wrap it in Option and use self.cb.take(). In this case, however, you'll have to check if the closure is still here anytime you try to call it.
If the closure is intended to be called multiple times, then it must not be FnOnce and therefore must not consume anything captured. In your case, you can simply make print_st take &str instead of String, add the referencing to the call site, and the error will go away - playground. But in your real use-case, this might be harder.