Generic function argument that accepts functions that implement a macro

Viewed 67

I'm using pyo3 to add some rust to my python project (for performance), and wanted to create a function to make adding submodules easier. My current code:

fn register_submodule(name: &str, python: Python, parent: &PyModule, funcs: Vec<?>) -> PyResult<()> {
    let name = &(parent.name()?.to_owned() + name);
    let child = PyModule::new(python, name)?;

    for func in &funcs {
        child.add_function(wrap_pyfunction!(func, child)?)?;
    }

    py_run!(python, child, format!("import sys; sys.modules[{}] = child_module", name).as_str());
    parent.add_submodule(child)?;

    Ok(())
}

I want this function to be able to take an array or vector (or whatever) containing functions that implement the #[PyFunction] macro, take any arguments of any type and return a PyResult containing any type, so I can register these to the new module. Is this possible? How would I go about doing it, or achieving a similar result? Any help would be much appreciated!

1 Answers

There is no way to do it, either no way to pass vector with different functions. Because functions in Vector may be very different, may accept different type/count of arguments (how rust will know how to call/use that functions).

The only way to achieve this is with Enums.

Create Enum with variants of all Python functions eg.

enum PyFunctionVariant {
   F1(fn(param1: i32, param2: i32) -> PyResult),
   F2(fn(param1: &str, param2: &str) -> PyResult),
   // all your function definitions
}

Change register_submodule declaration to accept Vec of PyFunctionVariant.

fn register_submodule(name: &str, python: Python, parent: &PyModule, funcs: Vec<PyFunctionVariant>) -> PyResult<()> {
   ...
}

You may also need to change this part with match, because now func is Enum not PyFunction.

for func in &funcs {
    child.add_function(wrap_pyfunction!(func, child)?)?; // Change Here
}

and use this way

let functions = vec![
   PyFunctionVariant::F1(add_int),
   PyFunctionVariant::F2(concat_str),
   // etc...
];
register_submodule("module", python, parent, functions)

Edit: There is also another way, You can use Vec<Box<dyn Any>> for passing vector of different functions. trait Any

Related