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

元气小坏坏 提交于 2020-05-14 20:08:10

问题


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.


回答1:


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



回答2:


To send a Rust object to C:

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

To borrow (and not free) from C:

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

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
}


来源:https://stackoverflow.com/questions/28278213/how-to-lend-a-rust-object-to-c-code-for-an-arbitrary-lifetime

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!