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);
}