fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(|x| x + 1, 5) // call
This function accepts both, closures and function pointers. It takes a function pointer as parameter type.
When should I prefer this over using a trait object, like &dyn Fn(i32) -> i32 or Box<dyn Fn(i32)-> i32> instead of fn
fn do_twice(f: &dyn Fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(&|x| x + 1, 5) // call
or
fn do_twice(f: Box<dyn Fn(i32) -> i32>, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
When should I prefer this over using a trait object
Trait objects are not the only other option. As @DarthKotik pointed out, accepting a fn pointer will not permit closures that capture their environment, but you can just use a normal type parameter, bounded by Fn to accept both functions and closures, without needing to box anything:
fn do_twice<F>(f: F, arg: i32) -> i32
where
F: Fn(i32) -> i32
{
f(arg) + f(arg)
}
Or, equivalently, but avoiding an extra type variable:
fn do_twice(f: impl Fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn type is a bare function pointer (https://doc.rust-lang.org/std/primitive.fn.html).
It won't work with the closure that captures environment and it cannot be implemented manually for your fancy type (like impl Fn for MySuperType)
So the only reason why your examples working is the fact that it's oversimplified!
if you make it just a bit more complicated, it will fail https://gist.github.com/rust-play/2167e73325daa1e2a179505209405917
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