如何在任意生命周期内将 Rust 对象借给 C 代码?

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

我正在用 Rust 编写一个具有 C 接口的库。 C 端必须能够创建和销毁 Rust 对象(C 端拥有它们并控制它们的生命周期)。

我已经设法 "leak" 一个 C 对象,但我不确定如何正确释放它:

pub extern "C" fn create() -> *mut Foo {
   让 obj = Foo; // 哎呀,一个错误
   让 ptr = std::mem::transmute(&mut obj); // 坏的
   std::mem::forget(对象); // 没有必要
   return ptr;
}

pub extern "C" fn destroy(handle: *mut Foo) {
   // 取回 Foo 并丢弃它???
}

我不确定如何将指针转回 Rust 将调用 Drop 的对象。简单地取消引用 *handle 不会编译。

实际上,您还没有设法将对象泄露给 C;您已经设法泄露了对(不久)不存在的堆栈框架的引用。 :D

这是一个应该可以正常工作的完整示例。我已尝试对其进行适当的评论,以解释我在做什么以及为什么这样做。

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

发送 Rust object 到 C:

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

或者利用 Box 是 FFI-safe 并且与指针相同,并且 Rust 函数定义不必完全匹配 C headers 的事实,只要ABI 相同:

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

(返回 Option<Box<Foo>> 也可以。Result 不行。)

从 C:

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

或利用引用和 Option 成为 FFI-safe:

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

取 over/destroy Rust object 以前给 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
}

或者使用 Rust 的 Option<Box> 是 FFI-safe 和 memory-managed 的事实:

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