Okay, I'm trying to achieve the following:
- C calls into rust
- rust calls back into c and registers a callback on a user defined trait object
- c calls into rust with the context
- rust calls the callback on the context (trait object)
I've been playing around with it quite a bit. I got quite far, but still not quite there.
The C bit:
#include <dlfcn.h>
#include <stdio.h>
void *global_ctx;
void c_function(void* ctx) {
printf("Called c_function\n");
global_ctx = ctx;
}
int main(void) {
void *thing = dlopen("thing/target/debug/libthing.dylib", RTLD_NOW | RTLD_GLOBAL);
if (!thing) {
printf("error: %s\n", dlerror());
return 1;
}
void (*rust_function)(void) = dlsym(thing, "rust_function");
void (*rust_cb)(void*) = dlsym(thing, "rust_cb");
printf("rust_function = %p\n", rust_function);
rust_function();
rust_cb(global_ctx);
}
The rust bit:
extern crate libc;
pub trait Foo {
fn callback(&self);
}
extern {
fn c_function(context: *mut libc::c_void);
}
pub struct MyFoo;
impl Foo for MyFoo {
fn callback(&self) {
println!("callback on trait");
}
}
#[no_mangle]
pub extern fn rust_cb(context: *mut Foo) {
unsafe {
let cb:Box<Foo> = Box::from_raw(context);
cb.callback();
}
}
#[no_mangle]
pub extern fn rust_function() {
println!("Called rust_function");
let tmp = Box::new(MyFoo);
unsafe {
c_function(Box::into_raw(tmp) as *const Foo as *mut libc::c_void);
}
}
The issue:
- My program segfaults when I try to call "callback" on the trait object in "rust_cb"
One Solution: - Change the function signature of "rust_cb" to
pub extern fn rust_cb(context: *mut MyFoo)
but that's not what I want, as I'm trying to create a safe wrapper that only knows the trait of the listener
Any help appreciated
PS: my assumption is that it segfaults, because the compiler doesn't know the offset of callback on the trait Foo, it needs the actual object to determine where it is. but then i have no idea how to work around that
So, if you need to represent the
Foo
as avoid *
, you can use this:I think you may be misunderstanding what a trait object is. A trait object is a type that is the size of two pointers (so, 128 bits on a 64-bit system). In this example,
Foo
is not a trait object, it is a dynamically sized type (i.e. a type which has a variable size, such asstr
).Box<Foo>
is a trait object.Box<Box<Foo>>
is neither a trait object or a dynamically sized type, it is a type that has the same size as a pointer, which is why we need to use it here since we want to convert it into avoid *
.I call it "leaking" because when you call
Box::into_raw
, you are leaking the memory of whatever is in the box, which means that that you are responsible for making sure the destructor (theDrop
implementation) gets called.Rust trait objects such as
Box<Foo>
are double the size of a normal pointer, so you can't usevoid *
to represent them. Seestd::raw::TraitObject
for more information. Here is a working version of your code:program.c:
lib.rs:
This will only work on nightly rustc (because of the
#![feature(raw)]
) and will also give a warning becauseTraitObject
isn't FFI-safe. If you want something that will work on stable, you can define some struct of an appropriate size like this and use it instead ofTraitObject
:Another option, of course, would just be to use
Box<Foo>
in place ofTraitObject
, but then you would still get a warning:If you really want to use a
void *
, you could consider leaking theTraitObject
as well as theMyFoo
and use two levels of indirection.