161

Can I pass a function as a parameter? If not, what is a good alternative?

I tried some different syntaxes but I have not found the right one. I know I can do this:

fn example() {
    let fun: fn(value: i32) -> i32;
    fun = fun_test;
    fun(5i32);
}

fn fun_test(value: i32) -> i32 {
    println!("{}", value);
    value
}

but that's not passing the function as a parameter to another function:

fn fun_test(value: i32, (some_function_prototype)) -> i32 {
    println!("{}", value);
    value
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Angel Angel
  • 19,670
  • 29
  • 79
  • 105

2 Answers2

233

Sure you can:

fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    fun_test(5, &times2);
}

As this is Rust, you have to take into account the ownership and lifetime of the closure.

TL;DR; Basically there are 3 types of closures (callable objects):

  1. Fn: It cannot modify the objects it captures.
  2. FnMut: It can modify the objects it captures.
  3. FnOnce: The most restricted. Can only be called once because when it is called it consumes itself and its captures.

See When does a closure implement Fn, FnMut and FnOnce? for more details

If you are using a simple pointer-to-function like closure, then the capture set is empty and you have the Fn flavor.

If you want to do more fancy stuff, then you will have to use lambda functions.

In Rust there are proper pointers to functions, that work just like those in C. Their type is for example fn(i32) -> i32. The Fn(i32) -> i32, FnMut(i32) -> i32 and FnOnce(i32) -> i32 are actually traits. A pointer to a function always implements all three of these, but Rust also has closures, that may or may not be converted to pointers (depending on whether the capture set is empty) to functions but they do implement some of these traits.

So for example, the example from above can be expanded:

fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_ptr(value: i32, f: fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    let y = 2;
    //static dispatch
    fun_test_impl(5, times2);
    fun_test_impl(5, |x| 2*x);
    fun_test_impl(5, |x| y*x);
    //dynamic dispatch
    fun_test_dyn(5, &times2);
    fun_test_dyn(5, &|x| 2*x);
    fun_test_dyn(5, &|x| y*x);
    //C-like pointer to function
    fun_test_ptr(5, times2);
    fun_test_ptr(5, |x| 2*x); //ok: empty capture set
    fun_test_ptr(5, |x| y*x); //error: expected fn pointer, found closure
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 1
    there is a difference in using or not (.., f: &Fn...) the two works, some detail that I need to know? – Angel Angel Apr 03 '16 at 21:40
  • @AngelAngel: Well, `Fn*` are traits, so the usual `` vs `(t: &T)` applies. The main limitation of the non-generic solution is that it must be used with references. So if you want `FnOnce`, which should be passed as a copy, you must use the generic style. – rodrigo Apr 03 '16 at 21:55
  • 8
    Note that it is more idiomatic to use generics instead of trait objects (i.e. `` instead of `(f: &Fn...)`. And this is for a reason - generics will result in static dispatch, while trait objects require dynamic dispatch. – Vladimir Matveev Apr 03 '16 at 22:12
  • 3
    Interestingly, from an *interface* (caller's) perspective, `FnOnce` is actually the most generic trait- it accepts all closures regardless of whether they read, modify, or take ownership of the captured state. `FnMut` is more restrictive, it doesn't accept closures that take ownership of a captured object (but it still allows modifications of state). `Fn` is the most restrictive because it doesn't accept closures that modify their captured state. So requiring `&Fn` places the greatest restriction on the `funTest` caller,while providing the least restriction on how `f` can be invoked inside it. – user4815162342 Sep 26 '17 at 07:12
57

Fn, FnMut and FnOnce, outlined in the other answer, are closure types. The types of functions that close over their scope.

Apart from passing closures Rust also supports passing simple (non-closure) functions, like this:

fn times2(value: i32) -> i32 {
    2 * value
}

fn fun_test(value: i32, f: fn(i32) -> i32) -> i32 {
    println!("{}", f (value));
    value
}

fn main() {
    fun_test (2, times2);
}

fn(i32) -> i32 here is a function pointer type.

If you don't need a full-fledged closure than working with function types is often simpler as it doesn't have to deal with those closure lifetime nicities.

ArtemGr
  • 11,684
  • 3
  • 52
  • 85
  • @IvanTemchenko Maybe? Here is some code for you to play with: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=41d2aa2c947dbebe539e5effc7310ccd – ArtemGr Dec 18 '20 at 06:25
  • that's not exactly what I meant =) Found workaround to return dyn closure which captures self's state so I dont need to pass instance ref around... – Ivan Temchenko Dec 18 '20 at 08:36