在 C++ 和 Rust 之间来回传递 Rc<RefCell<T>>

Luc*_*lla 4 rust

根据我的这个问题的答案:如何在通过C++创建的Rust代码中保存Rust对象?我可以将 Rust 中在 a 中分配的东西传递回 C Box,然后再次将其作为对 的引用接收回来&T,因为 Rust 分配的Sized结构遵循 C ABI。

我想做同样的事情,但现在Rc<RefCell<T>>。我应该返回 aBox到吗Rc<RefCell<T>>?我猜它不会,因为Rc没有实现,这是根据Box 页面所Sized必需的。所以这行不通:TBox<T>

#[no_mangle]
pub extern "C" fn foo_new() -> Box<Rc<RefCell<T>>> {
    Box::new(Foo { glonk: false })
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?基本上,我需要创建一个可以被许多人访问的 Rust 结构,并且可以被其中之一可变地借用。这就是我选择的原因Rc<RefCell<T>>。是否有另一种类型的结构可以满足我的要求并且对 C 语言友好?

E_n*_*ate 7

它们实现了智能指针Rc<T>Box<T> 确实大小的类型Sized。此信息未在文档中提供,可能是因为 的特殊性质Sized:它始终自动派生所有适用的类型,并且无法添加或取消该实现(与 和 不同SendSync

排除了这一点,将另一个智能指针封装到Box. 如果打算将实际智能指针 ( Rc) 跨过 C FFI 边界并返回,则只需记住一件事: an 的布局Rc<T>与原始指针不兼容,即使TSized与 不同Box<T>)。

因此,我们需要将其显式地转换为原始指针并返回。函数into_rawfrom_raw也可用于Rc.

/// create a new Foo and give it to caller
#[no_mangle]
pub extern "C" fn foo_new() -> *const RefCell<Foo> {
    Rc::into_raw(Rc::new(RefCell::new(Foo { glonk: false })))
}

/// clone the pointers into two
#[no_mangle]
pub extern "C" fn foo_clone(foo: *const RefCell<Foo>) -> (*const RefCell<Foo>, *const RefCell<Foo>) {
    unsafe {
        let ptr = Rc::from_raw(foo);
        let c = Rc::clone(ptr);
        (ptr, c)
    }
}

/// get a property of a Foo without returning ownership
#[no_mangle]
pub extern "C" fn foo_glonk(foo: *const RefCell<Foo>) -> bool {
    unsafe { (*foo).borrow().glonk }
}

/// return ownership of the Foo,
/// so it can be freed in Rust-land
#[no_mangle]
pub extern "C" fn foo_return(foo: *const RefCell<Foo>)  {
    let _ = Rc::from_raw(foo);
}
Run Code Online (Sandbox Code Playgroud)