How to convert Box<dyn Fn()> into raw pointer and then call it?

Hello.

I'm trying to pass a closure into a C function. As often seen, the C function takes a callback function and an argument. I do it like this:

type Callback = extern "C" fn(*mut ());

// Implemented somewhere in a library
unsafe fn some_c_function(callback: Callback, argument: *mut ()) {
    callback(argument);
}

extern "C" fn my_callback(argument: *mut ()) {
    unsafe {
        let closure = argument as *mut dyn Fn();
        (*closure)();
    }
}

fn main() {
    let closure: Box<dyn Fn()> = Box::new(|| println!("hello"));
    let closure_raw = Box::into_raw(closure);

    unsafe {
        some_c_function(my_callback, closure_raw as *mut ());
    }
}

But the compiler gives me the following error:

error[E0277]: expected a `Fn<()>` closure, found `()`
  --> src/main.rs:10:23
   |
10 |         let closure = argument as *mut dyn Fn();
   |                       ^^^^^^^^ expected an `Fn<()>` closure, found `()`
   |
   = help: the trait `Fn<()>` is not implemented for `()`
   = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
   = note: required for the cast from `*mut ()` to `*mut dyn Fn()`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `testrust` (bin "testrust") due to previous error

Using core::mem::transmute doesn't help:

let closure: *mut dyn Fn() = core::mem::transmute(argument);
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
  --> src/main.rs:10:38
   |
10 |         let closure: *mut dyn Fn() = core::mem::transmute(argument);
   |                                      ^^^^^^^^^^^^^^^^^^^^
   |
   = note: source type: `*mut ()` (64 bits)
   = note: target type: `*mut dyn Fn()` (128 bits)

For more information about this error, try `rustc --explain E0512`.
error: could not compile `testrust` (bin "testrust") due to previous error

It seems like *mut dyn Fn() size is twice the size of *mut (). I suspect it might be due to the dyn dynamic dispatch and the presence of a vtable, but in any case I still can't find a way to call the boxed closure casted to a raw pointer.

Hope for your help!

One way to do this is to wrap it in another box:

type Callback = extern "C" fn(*mut ());

// Implemented somewhere in a library
unsafe fn some_c_function(callback: Callback, argument: *mut ()) {
    callback(argument);
}

extern "C" fn my_callback(argument: *mut ()) {
    unsafe {
        let closure = argument as *mut Box<dyn Fn()>;
        
        // Or, to destroy the box:
        // let closure = Box::from_raw(argument as *mut Box<dyn Fn()>);
        
        (*closure)();
    }
}

fn main() {
    let closure: Box<dyn Fn()> = Box::new(|| println!("hello"));
    let wrapped: Box<Box<dyn Fn()>> = Box::new(closure);
    let closure_raw = Box::into_raw(wrapped);

    unsafe {
        some_c_function(my_callback, closure_raw as *mut ());
    }
}

Additionally, if you haven't converted the closure into a dyn Fn() yet, but have it as an actual generic parameter, then you can do this:

type Callback = extern "C" fn(*mut ());

// Implemented somewhere in a library
unsafe fn some_c_function(callback: Callback, argument: *mut ()) {
    callback(argument);
}

extern "C" fn my_callback<F: Fn()>(argument: *mut ()) {
    unsafe {
        let closure = argument as *mut F;
        
        // Or, to destroy the box:
        // let closure = Box::from_raw(argument as *mut F);
        
        (*closure)();
    }
}

fn create_callback<F: Fn()>(func: F) -> (Callback, *mut ()) {
    (my_callback::<F>, Box::into_raw(Box::new(func)) as *mut ())
}

fn main() {
    let (callback, ptr) = create_callback(|| println!("hello"));

    unsafe {
        some_c_function(callback, ptr);
    }
}

This will generate a different my_callback function pointer for each type closure type F that you use it with.

4 Likes

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.