Storing arbitrary function callables into a hashmap in Rust

I am coming from Python and learning Rust. I am trying to store arbitrary functions (or pointers to functions) in a hashmap. And retrieve the function by string key and execute it. Basically trying to convert this simple Python code into Rust.

def foo(x, y):
    return x + y

def bar(x, y, z):
    return x + y + z

if __name__ == '__main__':
    dict = {}
    dict['foo'] = foo
    dict['bar'] = bar

    print(dict["foo"](1,2))

Because of the difference in the number of arguments, Rust compiler doesn’t accept this kind of code (I believe Rust needs to know exact size of the function in hashmap values, and it needs to be consistent across storing functions). So I am wondering if anyone has a correct way (or Rust way) to do this. I also tried with matching, but is failing with the same reason.

The closest I got in Rust was something like

fn foo(x: i32, y: i32) -> i32 {
    x + y
}

fn bar(x: i32, y: i32, z: i32) -> i32 {
    x + y + z
}

fn main() {
    let mut dict: std::collections::HashMap<&str, fn(i32, i32) -> i32> = std::collections::HashMap::new();
    dict.insert("foo", foo);
    dict.insert("bar", bar);

    println!("{}", (dict["foo"])(1, 2));
}

But this line dict.insert("bar", bar); isn’t compiling because the function bar has 3 arguments instead of 2.

>Solution :

You can store Box<dyn Any>:

fn main() {
    let mut dict: std::collections::HashMap<&str, Box<dyn std::any::Any>> =
        std::collections::HashMap::new();
    dict.insert("foo", Box::new(foo as fn(i32, i32) -> i32));
    dict.insert("bar", Box::new(bar as fn(i32, i32, i32) -> i32));

    let v = dict["foo"].downcast_ref::<fn(i32, i32) -> i32>().unwrap()(1, 2);
    println!("{v}");
}

However, I’d recommend you to rethink your design. Needing so much dynamism is rare in Rust, and perhaps your Python knowledge is leading you in the wrong direction.

Leave a Reply