According to the answer this question of mine: How to hold Rust objects in Rust code created through C++? I can pass back to C something allocated in Rust inside a Box
, and then receive it back again as a reference to &T
, because Rust allocates Sized
structs respecting the C ABI.
I want to do the same thing but now for Rc<RefCell<T>>
. Should I return a Box
to the Rc<RefCell<T>>
? My guess it no since Rc
does not implement Sized
, which is required for T
in Box<T>
according to the Box page. So this would not work:
#[no_mangle]
pub extern "C" fn foo_new() -> Box<Rc<RefCell<T>>> {
Box::new(Foo { glonk: false })
}
How can I make this work? Basically I need to create a Rust struct that can be accessed by many, and mutably borrowed by one of these many. That's why I choose Rc<RefCell<T>>
. Is there perhaps another type of structure that can do what I want and be C-friendly?
Both smart pointers Rc<T>
and Box<T>
are indeed sized types, they implement Sized
. This information is not presented in the documentation, probably because of the special nature of Sized
: it is always automatically derived for all types where it's applicable, and that implementation cannot be added or lifted (unlike Send
and Sync
).
With that out of the way, there is little benefit in encapsulating another smart pointer into a Box
. If there is an intention to move the actual smart pointer (Rc
) across the C FFI boundary and back, there is only one thing to keep in mind: the layout of an Rc<T>
is not compatible with a raw pointer, even when T
is Sized
(unlike Box<T>
).
Therefore, we need to convert it to a raw pointer and back explicitly. The functions into_raw
and from_raw
are also available for 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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With