Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I iterate over a vector of functions and call each of them?

Tags:

rust

I'm trying to use a for loop to iterate over a vector of functions and execute each function at each step.

fn f1(i: i32) -> i32 {
    i * 2
}

fn f2(i: i32) -> i32 {
    i * 4
}

fn main() {
    let mut arr: Vec<|i32| -> i32> = Vec::new();
    arr.push(f1);
    arr.push(f2);

    for f in arr.iter() {
        println!("{}", f(1));
    }
}

But the attempted execution of f(1) gives this error:

error: expected function, found '&|i32| -> i32'

I guess in putting the functions in the vector their type is mutated and no longer works like a normal function. Is there a way to transform it back, or am I missing something?

like image 279
Christopher Reid Avatar asked Dec 05 '14 08:12

Christopher Reid


1 Answers

As of Rust 1.x, unboxed closures are the only kind of closures in the language, and they don't need a feature flag. Moreover, static functions can easily be converted to unboxed closures. Therefore, the correct way to call functions from a vector of functions is:

fn f1(i: i32) -> i32 { i * 2 }

fn f2(i: i32) -> i32 { i * 4 }

fn main() {
    let arr: Vec<&dyn Fn(i32) -> i32> = vec![&f1, &f2];

    for f in &arr {
        println!("{}", (f)(1));
    }
}

I've used Fn() closures which can access their environment through a shared reference, so it is sufficient to iterate the vector by reference. If I had used a FnMut() closure, I would have had to use iteration by mutable reference:

fn f1(i: i32) -> i32 { i * 2 }

fn f2(i: i32) -> i32 { i * 4 }

fn main() {
    let p1 = &mut f1;
    let p2 = &mut f2;

    let mut arr: Vec<&mut dyn FnMut(i32) -> i32> = vec![p1, p2];

    for f in &mut arr {
        println!("{}", (f)(1));
    }
}

A similar idea holds for FnOnce() and iteration by value, although here we need to use Box to own the closure:

fn f1(i: i32) -> i32 { i * 2 }

fn f2(i: i32) -> i32 { i * 4 }

fn main() {
    let arr: Vec<Box<dyn FnOnce(i32) -> i32>> = vec![Box::new(f1), Box::new(f1)];

    for f in arr {
        println!("{}", (f)(1));
    }
}

Alternatively, if you know that you only work with static functions, it is possible to store pointers to them directly, without using closure traits:

fn f1(i: i32) -> i32 { i * 2 }

fn f2(i: i32) -> i32 { i * 4 }

fn main() {
    let arr: Vec<fn(i32) -> i32> = vec![f1, f2];

    for f in &arr {
        println!("{}", (f)(1));
    }
}

While f1 and f2 actually have different incompatible types, they are automatically coerced to the general function pointer type fn(i32) -> i32 when used in appropriate context, like in the example above.

Because static functions don't have any environment, you can freely clone references to them and call them through any kind of reference. This is probably the way to go if you only need static functions.

This answer was updated for Rust 1.x; older versions of the answer remain in the edit history.

like image 133
Vladimir Matveev Avatar answered Nov 11 '22 21:11

Vladimir Matveev