Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you pass a Rust function as a parameter?

Tags:

rust

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 } 
like image 676
Angel Angel Avatar asked Apr 03 '16 20:04

Angel Angel


People also ask

How do you call a function in Rust?

We define a function in Rust by entering fn followed by a function name and a set of parentheses. The curly brackets tell the compiler where the function body begins and ends. We can call any function we've defined by entering its name followed by a set of parentheses.

How do you pass by reference in Rust?

Syntax. An ampersand ( & ) is used with a variable's name while passing its reference instead of its value to a function. The function's signature should also have an ampersand with the type of argument that receives the reference.

Does Rust have function pointers?

In Rust, a function pointer type, is either fn(Args...)

What is FN in Rust?

Keyword fnFunctions are the primary way code is executed within Rust. Function blocks, usually just called functions, can be defined in a variety of different places and be assigned many different attributes and modifiers.


2 Answers

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 } 
like image 132
rodrigo Avatar answered Oct 07 '22 01:10

rodrigo


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.

like image 23
ArtemGr Avatar answered Oct 06 '22 23:10

ArtemGr