2

I've been working on a library recently and I want to make C bindings for it. One of the structs in this library returns a slice of trait objects to the user. This is what the function definition looks like:

pub fn controllers(&self) -> &[Box<dyn Controller>] {
    &self.controllers
}

I'm not sure how to go about translating this. The C code will not be poking inside these dynamic objects. It will simply be passing them back to rust code, i.e.

pub fn controller_get_name(controller: *const dyn Controller) -> *const c_char {
    let controller = controller.as_ref();
    controller.get_name().as_ptr();

}

Currently, I have this:

#[no_mangle]
pub extern "C" fn libvibrant_instance_get_controllers(instance: *mut Instance,
                                                      mut controllers: *const dyn Controller,
                                                      len: *mut usize) {
    assert!(!instance.is_null());
    assert!(!len.is_null());

    let instance = unsafe { instance.as_ref().unwrap() };
    controllers = instance.controllers().as_ptr();
    unsafe {
        *len = instance.controllers().len();
    }
}

But obviously, that doesn't work because as_ptr is returning a *const Box<dyn Controller> rather than *const dyn Controller. What can I do here?

zee
  • 2,933
  • 2
  • 16
  • 28

1 Answers1

1

When writing C bindings, you should think from the C side and what operations you want to provide, hiding the implementation as much as possible.

For instance, you say that the C code will call back Rust with the trait objects; and it seems like you want to pass them to C as a (pointer, length) pair so that C can iterate over them. However, that means C needs to know how big each trait object is (which is bigger than a single pointer; same for the shared slice).

Instead, I would provide an opaque handle to the collection and associated functions to iterate over it. Only if performance is extremely important I would let C know the size of each element (which means the ABI would depend on how Rust represents the DST).

For the technical details, take a look at questions like What is a “fat pointer” in Rust?.

Acorn
  • 24,970
  • 5
  • 40
  • 69