I am storing a closure in a struct like this:
#[derive(Clone)]
struct S<'a> {
func: &'a FnOnce() -> u32
}
fn main() {
let s = S { func: &|| 0 };
let val = (s.func)();
println!("{}", val);
}
When I compile, s.func
cannot be moved to execute itself. I understand why it cannot be moved (namely that it's only a reference and that its size is not known at compile time), but don't know why it's being moved at all -- is it just because closures are implemented via traits?
Here's the error message:
error[E0161]: cannot move a value of type std::ops::FnOnce() -> u32:
the size of std::ops::FnOnce() -> u32 cannot be statically determined
--> main.rs:8:15
|
8 | let val = (s.func)();
| ^^^^^^^^
error[E0507]: cannot move out of borrowed content
--> main.rs:8:15
|
8 | let val = (s.func)();
| ^^^^^^^^ cannot move out of borrowed content
error: aborting due to 2 previous errors
Is this only way the solve this to store the closure on the heap (via Box<FnOnce() -> u32>
)? And why does calling a closure move it? Presumably calling it doesn't mutate the function itself.
The closure is being moved because FnOnce::call_once
takes self
by value. This contract enforces the guarantee that the function will not be called more than once.
If you will indeed be calling the closure at most once, and you want to use the FnOnce
trait, then your struct needs to take ownership of that closure (and you will need to make your struct generic on the closure type). Note that calling the closure will move the closure out of your struct, thereby invalidating the whole struct; you may work around that by wrapping the FnOnce
in an Option
and take
-ing the closure out of the Option
before calling it.
If you might be calling the closure more than once, you don't want to take ownership of the closure, or you don't want to make your struct generic on the closure type, then you should use either Fn
or FnMut
instead. Fn::call
takes self
by reference and FnMut::call_mut
takes self
by mutable reference. Since both accept references, you can use trait objects with them.
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