Reputation: 1720
What is the correct function type for a "callback" function called inside a function?
fn or_else(value: u32, f: ???) -> u32 {
if (value == 0) {
f()
} else {
value
}
}
Example usage:
or_else(0, || { 3 })
What is rust angry when I use f: &dyn FnOnce() -> u32
?
Error:
cannot move a value of type dyn FnOnce() -> u32: the size of dyn FnOnce() -> u32 cannot be statically determinedrustcE0161
cannot move out of `*f` which is behind a shared reference
move occurs because `*f` has type `dyn FnOnce() -> u32`, which does not implement the `Copy` trait
I have trouble understanding E0161.
Upvotes: 0
Views: 78
Reputation: 43753
The most general way to accept a function, in this case, is to make your function generic, using either of these syntaxes:
fn or_else(value: u32, f: impl FnOnce() -> u32) -> u32 {
fn or_else<F: FnOnce() -> u32>(value: u32, f: F) -> u32 {
This is saying: f
can be of any type the caller cares to provide, as long as that type implements FnOnce
(is a function that can be called once).
What is rust angry when I use f:
&dyn FnOnce() -> u32
?
FnOnce
is named after the idea that it can only be called once, but what it actually means is that the function value is consumed (moved) by calling it. Thus, once you call the function, you don't have it any more.
But if you have &F
then you are not allowed to move the F
value, because you don't own it. So, it is impossible to call a FnOnce
by reference. (Similarly, a FnMut
requires a mutable reference, and cannot be called with an immutable reference.)
So, the type &dyn FnOnce
is useless — it can exist, but it can never be called. If you want to use dyn
(which can be a reasonable choice) then you have to use &dyn Fn
or &mut dyn FnMut
.
But, for a general-purpose function taking a callback, it is usually best to use generics instead of dyn
. This is because:
FnOnce
, the most general function traitFn
/FnMut
functions also implement Fn
/FnMut
themselvesUpvotes: 3