0
votes

I would like to store the pointer to a rust struct inside of it's member C struct. Is it required that the struct be enclosed in a Rc rather than a Box? The reason I'm asking is because although there is shared ownership here, the pointer is only ever accessed from within unsafe member functions of the Rust struct and the C struct's lifetime is tied to that of the enclosing Rust struct.

Here's an example ->

// C struct with constructor/destructor
struct c_foo {
    void*   internal; // pointer to rust `struct`
    /* ... */
};

struct c_foo* c_foo_new();
void c_foo_free(struct c_foo* foo);
// FFI struct generated by bindgen
#[repr(C)]
#[derive(Debug, Copy)]
pub struct Foo {
    pub internal: *mut libc::c_void, // pointer to rust `struct`
    /* ... */
}
// Rust struct that wraps the FFI struct
struct Bar {
    ptr: *mut Foo, // private
    /* ... */
}

impl Bar {
    fn new() -> Box<Bar> {
        unsafe {
            let mut bar = Box::new(Bar { ptr: c_foo_new() });
            let bar_ptr: *mut ffi::c_void = &mut bar as *mut _ as *mut ffi::c_void;
            (*bar.ptr).internal = bar_ptr;
            bar
        }
    }
}


impl Drop for Bar {
    fn drop(&mut self) {
        unsafe {
            c_foo_free((*bar.ptr).internal);
        }
    }
}

So there's a C struct c_foo with a void * that stores a reference to the Rust struct Bar. Foo is just the bindgen generated Rust wrapper for c_foo. Do I need a Box or Rc in the Bar::new() function?

To clarify, there is no shared ownership on the Rust side. There is shared ownership b/w the Rust and C side so I guess there is no benefit in using a Rc type.

1
It is unclear what you are trying to achieve with either Box or Rc. General guidance would be here, but why is it needed in the first place? You are already holding a pointer to a C struct. Does it have to be contained in another smart pointer? Moreover, the function Bar::new does not compile as it is. - E_net4
I need it because the C library provides callbacks, and I need a pointer to the Rust struct in order to be able to call the Rust callback function. This question is basically a follow-up to my previous question here -> stackoverflow.com/questions/60969071/… - Shoaib Ahmed
OK, but that code does return a Box<Bar>, whereas this version of Bar::new returns just a Bar. - E_net4
With that said, this one still applies on the Rust side: use Rc only if you need shared ownership in Rust code. Since C does not retain the semantics of this pointer type, C code needs to handle boundaries manually regardless. - E_net4
I fixed the code, Bar::new should indeed return Box<Bar>. But I think what you said in your next comment makes a lot of sense. That is what I was getting at when I wrote in the question that '... although there is shared ownership...'. So I guess I might as well handle it manually and just use a Box type. Your comment use Rc only if you need shared ownership in Rust code. clears my confusion. Thanks! - Shoaib Ahmed

1 Answers

-1
votes

E_net4 is renamed all the time's comment answers my question -

use Rc only if you need shared ownership in Rust code. Since C does not retain the semantics of this pointer type, C code needs to handle boundaries manually regardless.