Search code examples
crustffi

How to lend a Rust object to C code for an arbitrary lifetime?


I'm writing a library in Rust that has a C interface. C side must be able to create and destroy Rust objects (C side owns them and controls their lifetime).

I've managed to "leak" an object to C, but I'm not sure how to properly free it:

pub extern "C" fn create() -> *mut Foo {
   let obj = Foo; // oops, a bug
   let ptr = std::mem::transmute(&mut obj); // bad 
   std::mem::forget(obj); // not needed
   return ptr;
}

pub extern "C" fn destroy(handle: *mut Foo) {
   // get Foo back and Drop it??? 
}

I'm not sure how can I turn pointer back to an object that Rust will call Drop on. Simply dereferencing *handle doesn't compile.


Solution

  • To send a Rust object to C:

    #[no_mangle]
    pub extern "C" fn create_foo() -> *mut Foo {
        Box::into_raw(Box::new(Foo))
    }
    

    or taking advantage of Box being FFI-safe and the same as a pointer, and the fact that Rust function definitions do not have to match C headers exactly, as long as the ABI is the same:

    #[no_mangle]
    pub extern "C" fn create_foo() -> Box<Foo> {
       Box::new(Foo)
    }
    

    (returning Option<Box<Foo>> is fine too. Result is not.)

    To borrow (and not free) from C:

    #[no_mangle]
    pub unsafe extern "C" fn peek_at(foo: *mut Foo) {
        let foo = foo.as_ref().unwrap(); // That's ptr::as_ref
    }
    

    or taking advantage of references and Option being FFI-safe:

    #[no_mangle]
    pub extern "C" fn peek_at(foo: Option<&mut Foo>) {
        let foo = foo.unwrap();
    }
    

    To take over/destroy Rust object previously given to C:

    #[no_mangle]
    pub unsafe extern "C" fn free_foo(foo: *mut Foo) {
        assert!(!foo.is_null());
        Box::from_raw(foo); // Rust auto-drops it
    }
    

    or using the fact that Option<Box> is FFI-safe, and memory-managed by Rust:

    #[no_mangle]
    pub unsafe extern "C" fn free_foo(foo: Option<Box<Foo>>) {
       // dropped implicitly
    }