Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function pointer vs Fn trait object [duplicate]

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)
}
like image 662
Akshay Mariyanna Avatar asked Jan 14 '19 12:01

Akshay Mariyanna


2 Answers

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)
}
like image 162
Peter Hall Avatar answered Nov 17 '22 08:11

Peter Hall


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

like image 10
Darth Kotik Avatar answered Nov 17 '22 07:11

Darth Kotik