11
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)
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Thanks that did clarify my query. fn type parameters can only accept closures that do not capture environment So, should use trait object when no such constraint is intended – Akshay Mariyanna Jan 14 '19 at 13:00
  • For reference (in addition to the answers): [RFC 1558](https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md) proposed to automatically coerce closures that don't capture their environment to fn pointers. Thanks to this RFC, your example works (maybe unexpectedly). – Lukas Kalbertodt Jan 14 '19 at 14:08

2 Answers2

14

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)
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
10

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

Darth Kotik
  • 2,261
  • 1
  • 20
  • 29