如何在C++与Rust间传递Rc<RefCell<T>>及替代方案咨询
Rc<RefCell<T>> Created in Rust from C++? First, let's clear up a key misunderstanding: Rc<T> is actually Sized! The confusion probably comes from mixing up Sized requirements with dynamically sized types (DSTs) like slices or trait objects. Rc<T> itself is a fixed-size struct (it holds a pointer to the inner data and a reference count), so wrapping it in Box is totally valid. Your sample code fails not because of Sized issues, but likely because T isn't defined, or Foo doesn't match the return type you declared.
Correct Approach for Exposing Rc<RefCell<T>> to C
Here's how to properly wrap and expose your shared, mutable Rust struct to C:
Define your Rust struct and wrap it in
Rc<RefCell>:use std::cell::RefCell; use std::rc::Rc; #[derive(Debug)] struct Foo { glonk: bool, } #[no_mangle] pub extern "C" fn foo_new() -> *mut Rc<RefCell<Foo>> { // Create the shared mutable instance let rc = Rc::new(RefCell::new(Foo { glonk: false })); // Wrap it in a Box, then convert to a raw pointer (safe for C to hold) Box::into_raw(Box::new(rc)) }We return a raw pointer (
*mut Rc<RefCell<Foo>>) instead of aBoxdirectly because C doesn't understand Rust's smart pointers. The raw pointer is just a memory address, which C can store as avoid*or a typed pointer.Add functions to interact with the instance from C:
You need to expose safe methods to access and modify the inner data, since C can't safely handle Rust's borrow rules:#[no_mangle] pub extern "C" fn foo_toggle_glonk(ptr: *mut Rc<RefCell<Foo>>) { // Convert the raw pointer back to a Box (safe if we know it's valid) let mut rc = unsafe { Box::from_raw(ptr) }; // Borrow the inner data mutably and modify it rc.borrow_mut().glonk = !rc.borrow_mut().glonk; // Leak the Box back to a raw pointer so C can keep holding it std::mem::forget(rc); } #[no_mangle] pub extern "C" fn foo_get_glonk(ptr: *mut Rc<RefCell<Foo>>) -> bool { let rc = unsafe { Box::from_raw(ptr) }; let value = rc.borrow().glonk; std::mem::forget(rc); value } #[no_mangle] pub extern "C" fn foo_destroy(ptr: *mut Rc<RefCell<Foo>>) { // Properly clean up the Rc instance when C is done with it unsafe { Box::from_raw(ptr); } }In C++, you can use these functions like this:
#include <cstdint> #include <cstdio> extern "C" { void* foo_new(); void foo_toggle_glonk(void* ptr); bool foo_get_glonk(void* ptr); void foo_destroy(void* ptr); } int main() { void* foo = foo_new(); foo_toggle_glonk(foo); printf("Glonk value: %d\n", foo_get_glonk(foo)); foo_destroy(foo); return 0; }
Key Safety Notes
- Never manually free the pointer in C: Always use the
foo_destroyfunction to let Rust handle theRccleanup (this ensures the reference count drops correctly and the inner data is deallocated when appropriate). - Validate pointers: In real code, you should add checks to ensure the raw pointer isn't null before dereferencing it in Rust (use
ptr.is_null()and return an error code if needed). - Borrow rules still apply: Even though
RefCellallows dynamic borrowing, you'll get a panic if you try to borrow mutably while an immutable borrow exists. Make sure your C++ code doesn't trigger this (e.g., don't callfoo_toggle_glonkwhile another function is holding an immutable borrow).
Alternative Structures for C Interoperability
If you're looking for alternatives that might be more ergonomic for C/C++:
Arc<Mutex<T>>instead ofRc<RefCell<T>>: If you need thread-safe shared access (since C++ code might be multi-threaded),Arcis the thread-safe counterpart toRc, andMutexprovides thread-safe mutable access.- Custom opaque handle: Instead of exposing the actual
Rc<RefCell<T>>pointer, you can wrap it in a unit struct (e.g.,struct FooHandle(*mut Rc<RefCell<Foo>>);) and return that as an opaque type to C. This prevents C code from accidentally modifying the pointer or accessing internal data directly. - Raw pointers with manual management: If you don't need shared ownership, you could pass a raw pointer to
RefCell<T>or even a mutable raw pointer toT, but this requires extremely careful manual control of lifetimes and borrows (easy to introduce unsafety).
内容的提问来源于stack exchange,提问作者PPP

