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

I am writing a library in Rust that has a C interface. The C side should be able to create and destroy Rust objects (the C side owns them and controls their lifespan).

I managed to β€œskip” the object in C, but I'm not sure how to release it correctly:

  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 I can turn the pointer back to the object that Rust will call Drop. Just dereferencing *handle does not compile.

+5
source share
2 answers

In fact, you were not able to skip the object in C; you were able to test a link to a (briefly) non-existent stack frame .: D

Here is a complete example that should work correctly. I tried to somehow comment on this to explain what I am 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())); // * derefs the Box into a Dramatic, the &mut re-borrows it into a regular // reference. The constraint ensures we coerce the &mut Dramatic into // a *mut Dramatic, which "hides" the reference from the borrow checker. let ptr: *mut _ = &mut *obj; // Forget discards its argument (passed by-move), without trigger its // destructor, if it has one. ::std::mem::forget(obj); ptr } 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. let obj: Box<Dramatic> = unsafe { ::std::mem::transmute(*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. ::std::mem::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); } 
+6
source

To send a Rust object to C:

<Preview> # [No_mangle] pub extern "C" fn create_foo () β†’ * mut Foo { Box :: in_raw (Box :: new (Foo))}

Borrow (and not free) from C:

<Preview> # [No_mangle] pub extern "C" fn do (foo: * mut Foo) β†’ * mut Foo {let foo = foo. as_ref () .unwrap (); // What ptr :: as_ref }

To take / destroy the Rust object previously set by C:

<Preview> # [No_mangle] pub extern "C" fn free_foo (foo: * mut Foo) { Box :: from_raw (foo); // Rust automatically discards}
+3
source

Source: https://habr.com/ru/post/1212436/


All Articles