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:
- it allows you to accept
FnOnce, the most general function trait
- it allows the function to be passed by value or by reference, if that is possible for the specific function, because references to
Fn/FnMut functions also implement Fn/FnMut themselves
- it allows the compiler to inline the callback function so the code can be optimized as a whole