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

4
根据我提出的这个问题的答案: 如何在C++中创建的Rust代码中保留Rust对象? 我可以把在Rust中分配的东西通过Box传回C,然后再次接收它作为对&T的引用,因为Rust会按照C ABI分配Sized结构体。

我想做同样的事情,但现在是针对Rc<RefCell<T>>。我应该返回Rc<RefCell<T>>Box吗?我的猜测是否定的,因为Rc没有实现Sized,而Box<T>根据Box页面需要T实现Sized。因此,这种方法行不通:

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

我该怎样让它工作?基本上,我需要创建一个Rust结构体,可以被许多人访问,并且可以被这些人中的一个可变地借用。这就是为什么我选择了Rc<RefCell<T>>。也许还有另一种类型的结构可以做到我想要的,并且对C友好吗?

1
Rc确实实现了Sized。您是如何得出相反的结论的? - E net4
@E_net4theclosevoter 我在 Rust 网站上的 Rc 页面上找不到 impl Sized for Rc。所以我应该使用 Box<Rc<RefCell<T>>> 吗?我不能简单地使用 Rc<RefCell<T>> 吗?我找不到它是否具有与 Box 相同的属性(即,其内存布局类似于 C 布局)。 - PPP
1个回答

7

智能指针Rc<T>Box<T>确实是大小已知的类型,它们都实现了Sized。这些信息在文档中没有呈现,可能是因为Sized 的特殊性质:它始终会自动派生适用于所有类型,该实现不能添加或提升(不像SendSync)。

这样一来,将另一个智能指针(Rc)封装到Box中并没有太多好处。如果有意图将实际的智能指针(Rc)跨越 C FFI 边界来回移动,只需要记住一件事:即使当TSized时(Box<T>不同),Rc<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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接