Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a function as argument in Rust

Given the following rust program:

fn call_twice<A>(val: A, f: fn(A) -> A) -> A {
    f(f(val))
}

fn main() {
    fn double(x: int) -> int {x + x};
    println!("Res is {}", call_twice(10i, double));
    // println!("Res is {}", call_twice(10i, (x: int) -> int {x + x}));
    // ^ this line will fail
}

Why can I pass double as the function, but not inlined? What is a good way to achieve the same behaviour without defining the function somewhere?

like image 593
Martin Seeler Avatar asked Nov 22 '14 17:11

Martin Seeler


People also ask

How do you pass a function as an argument?

We cannot pass the function as an argument to another function. But we can pass the reference of a function as a parameter by using a function pointer. This process is known as call by reference as the function parameter is passed as a pointer that holds the address of arguments.

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.

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.

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.


1 Answers

2016-04-01 Update:

As of Rust 1.0, the code should look like this:

fn call_twice<A, F>(val: A, mut f: F) -> A
where F: FnMut(A) -> A {
    let tmp = f(val);
    f(tmp)
}

fn main() {
    fn double(x: i32) -> i32 {x + x};
    println!("Res is {}", call_twice(10, double));
    println!("Res is {}", call_twice(10, |x| x + x));
}

The change to the closure parameter is because closure are now unboxed.

Original:

Insofar as I know, you can't define functions inline like that.

What you want is a closure. The following works:

fn call_twice<A>(val: A, f: |A| -> A) -> A {
    let tmp = f(val);
    f(tmp)
}

fn main() {
    fn double(x: int) -> int {x + x};
    println!("Res is {}", call_twice(10i, double));
    println!("Res is {}", call_twice(10i, |x| x + x));
}

There are a few things to note:

  1. Functions coerce to closures, but the opposite isn't true.

  2. You need to store the result of f(val) in a temporary due to borrowing rules. Short version: you need unique access to a closure to call it, and the borrow checker isn't quite clever enough to realise the two calls are independent in their original positions.

  3. Closures are in the process of being replaced by unboxed closures, so this will change in the future, but we're not quite there yet.

like image 75
DK. Avatar answered Oct 14 '22 17:10

DK.