You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++与Rust间传递Rc<RefCell<T>>及替代方案咨询

How to Hold a Rust 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:

  1. 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 a Box directly because C doesn't understand Rust's smart pointers. The raw pointer is just a memory address, which C can store as a void* or a typed pointer.

  2. 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); }
    }
    
  3. 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_destroy function to let Rust handle the Rc cleanup (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 RefCell allows 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 call foo_toggle_glonk while 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 of Rc<RefCell<T>>: If you need thread-safe shared access (since C++ code might be multi-threaded), Arc is the thread-safe counterpart to Rc, and Mutex provides 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 to T, but this requires extremely careful manual control of lifetimes and borrows (easy to introduce unsafety).

内容的提问来源于stack exchange,提问作者PPP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:17:28