Generic callback function - clunky version

Ref:

Rust does not allow a generic type parameter in a function parameter. At least not yet.
I need to do that, though, so here's a solution involving Box, RefCell, and run-time borrow().
It works, but is a bit clunky. Suggestions?

I tried using just RefCell, which, like Box, takes ownership of its content. But RefCell<&dyn mut Any> doesn't work.

(Use case: passing a mutable link to a MySQL connection into something that uses it repeatedly.)

You can have generic type parameters in functions, even in function pointers used as callbacks, as long as you can choose one type when making the function pointer.

fn callback<T>(t: &T) {}

let ptr: fn(&i32) = callback;
let ptr: fn(&f64) = callback;

If you need a callback for C FFI, and your callback knows what type it wants, then don't bother with dyn Any, just do an unsafe cast:

fn callback(t: &ComplexRustType) {}

let ptr = unsafe { 
    std::mem::transmute::<fn(&ComplexRustType), fn(*const c_void)>(callback) 
};

When the &ComplexRustType is a fat pointer, like &dyn Trait, then you need to make it thin by using &&dyn Trait.

Usually the callback shouldn't be getting a Box type, because it will then take ownership of it and free it. That makes sense only when the callback is guaranteed to be called exactly once. But when allocating data for C FFI, you'll need a stable pointer, so the data is likely to be held somewhere on the Rust side as Box<ComplexRustType>. If the type is a fat pointer, you'll need double indirection when allocating Box<Box<dyn Trait>>. For the function argument, &Box<dyn Trait> and &&dyn Trait are going to be equivalent.


If you need a safe Rust generic callback not for FFI, then don't bother with extra layers of indirection, &dyn Any is all you need:

fn callback(a: &dyn Any) {
    let s = a.downcast_ref::<RefCell<String>>();
}
4 Likes

How did C FFI get into this?

I should probably do this as a trait. I was following the Python and Go APIs, but they're a bad fit to Rust.

I assumed FFI, because otherwise Rust gives you many ways of using generics.

Callbacks in Rust can use Fn traits. It could be Fn(T) too if you don't need to make it a dyn object.

Rust callbacks can be closures capturing their environment, so they usually don't need to take arbitrary user-supplied types like FFI.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.