How to lend a Rust object to C code for an arbitrary lifetime?
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
}
Actually, you haven't managed to leak an object to C; you've managed to leak a reference to a (shortly) non-existent stack frame. :D
Here's a full example that should work correctly. I've tried to comment it as appropriate to explain what I'm doing and why.
pub struct Dramatic(String);
// Implement a destructor just so we can see when the object is destroyed.
impl Drop for Dramatic {
fn drop(&mut self) {
println!("And lo, I, {}, meet a most terrible fate!", self.0);
}
}
pub extern "C" fn create() -> *mut Dramatic {
// We **must** heap-allocate the object! Returning a reference to a local
// will **almost certainly** break your program!
let mut obj = Box::new(Dramatic("Roger".to_string()));
// into_raw turns the Box into a *mut Dramatic, which the borrow checker
// ignores, without calling its destructor.
Box::into_raw(obj)
}
pub extern "C" fn destroy(ptr: &mut *mut Dramatic) {
// First, we **must** check to see if the pointer is null.
if ptr.is_null() {
// Do nothing.
return;
}
// Now we know the pointer is non-null, we can continue. from_raw is the
// inverse of into_raw: it turns the *mut Dramatic back into a
// Box<Dramatic>. You must only call from_raw once per pointer.
let obj: Box<Dramatic> = unsafe { Box::from_raw(*ptr) };
// We don't *have* to do anything else; once obj goes out of scope, it will
// be dropped. I'm going to drop it explicitly, however, for clarity.
drop(obj);
// I am, however, going to null out the `ptr` we were passed just so the
// calling code is less likely to accidentally re-use the pointer.
*ptr = ::std::ptr::null_mut();
}
fn main() {
let mut ptr = create();
println!("ptr = {:?}", ptr);
destroy(&mut ptr);
println!("ptr = {:?}", ptr);
}