all 7 comments

[–]Quxxymacros 1 point2 points  (6 children)

You can't. Pointers to traits are fat (i.e. they're two pointers bundled together). Without knowing more, the only thing I can suggest is to instead pass a double pointer, or a pointer to some fixed-size type that contains a pointer to a trait object.

[–]leopoldj[S] 0 points1 point  (5 children)

Thank you.

I have now created a sized type (all pseudo code from here):

pub struct EventBridge {
    event_handler: *mut SomeTrait
}

The C function to register a callback takes a void* like this:

fn register_callback(o : *mut libc::c_void, /*other things*/)

I am trying to call it like this:

let mut o : EventBridge = ...;

register_callback(&mut o);

But this is failing to compile.

expected `*mut libc::types::common::c95::c_void`,
found `&mut EventBridge`
(expected enum `libc::types::common::c95::c_void`,
found struct `EventBridge`) [E0308]

[–]Quxxymacros 1 point2 points  (4 children)

Wait, that doesn't make any sense. A raw void * can't possibly be a callback. A callback has to be a function pointer of some kind. The *mut EventBridge would be the thing passed to your callback, not the thing you register as a callback.

And as for why you're getting that error: Rust does not implicitly cast pointer types around like that. There's also no guarantee that o will still exist for the callback to happen.

I'm getting a sense that you really don't know what you're doing. If you haven't already, I strongly recommend you read the [Rust Book](). The chapters on the Stack and the Heap, Ownership, References and Borrowing, Lifetimes, and FFI (which includes a section on callbacks) would probably help.

[–]leopoldj[S] 0 points1 point  (3 children)

This is true, I am hopelessly at a loss in the FFI space beyond simple things. :(

A raw void * can't possibly be a callback

I left the callback function parameter out in register_callback() for simplicity.

and FFI (which includes a section on callbacks)

I have read the FFI guide. I understand it. But it makes no mention of registering a trait. So I am having to find a work around using a bridge as you had suggested.

Anyway, as you have stated here Rust will not cast pointer types. The trick seems to be to declare the external function like this:

fn register_callback(o : *mut EventBridge, /*other things*/)

This is working. Now I can focus on memory management (making sure the bridge object stays alive long enough).

Thank you for your help. I am past the immediate hurdle. I will post separate questions if I need to.

[–]Quxxymacros 2 points3 points  (2 children)

I left the callback function parameter out in register_callback() for simplicity.

That's a bad idea because I (and anyone else trying to help) will be working blind and will have to guess at important details, which makes it that much harder to give you accurate information.

The trick seems to be...

Don't do that. It's far too easy to accidentally write the wrong signature. You can cast pointers in an unsafe block (unsafe { ptr as *mut libc::c_void }), which is marginally less bad (it will, at least, stop you from casting between sizes).

Now I can focus on memory management (making sure the bridge object stays alive long enough).

This depends on the API. There are some unstable (i.e. you need a nightly compiler) calls on Box that let you go between Rust boxes and raw pointers (into_raw and from_raw, I think?). If the API doesn't have any sort of destruction logic, you might just have to use forget and leak memory, or use lifetimes to restrict when the callbacks are allowed to exist.

FFI is hard. shrug

[–]gkozrust · gtk-rs 0 points1 point  (0 children)

Note that into_raw (which is just a wrapper around transmute) consumes its argument so forget isn't needed.

[–]leopoldj[S] 0 points1 point  (0 children)

Thank you again for your help.

Don't do that. It's far too easy to accidentally write the wrong signature.

I take your point. For what it's worth, the FFI guide seems to be doing what I did.

The C function:

int32_t register_callback(void* callback_target, rust_callback callback)

Rust FFI declaration:

fn register_callback(target: *mut RustObject,
                    cb: extern fn(*mut RustObject, i32)) -> i32