I want to implement a FFI for C-Code which calls Functions written in Rust and stores results in Rust-objects for later use.
- The Rust-Code returns references to objects as
Arc<T>whereTequals the type of the Rust-objects storing the date. - The C-Code has to create new Rust-objects, read out their data and free the memory when it's done.
- To that end, I cast the
Arc<T>into a raw pointer*const Tand pass this pointer to C I learned that due to the ABI compatibility, C can interpret*const Tas a*voidpointer. My concern is the idiomatic and memory safe handling of the Arc and it's referenced object within C.
So far, I've come up with this
use std::sync::Arc;
pub struct my_struct {
// some fields
}
pub fn init_my_struct(/*some parameters*/) -> Arc<my_struct> {
// do stuff
new_struct // new struct is of type Arc<my_struct> with field values initialized
}
pub extern "C" fn c_wrap_init_my_struct(/*some parameters*/) -> *const my_struct {
// cast to raw pointer, hold by C
Arc::into_raw(init_my_struct(/*some parameters*/))
}
pub extern "C" fn c_wrap_get_data_from_struct(my_struct_ptr: *const my_struct) -> /*some data type*/
{
// reconstruct the rust object to extract a value
// increment strong counter before re-constructing the object from pointer
unsafe { Arc::increment_strong_count(my_struct_ptr) };
// mys has ref count two due to increment above
let mys = unsafe { Arc::from_raw(my_struct_ptr) };
// do stuff to extract value from mys
value // return value
} // mys goes out of scope and ref count of my_struct_ptr is decremented to one (2-1=1).
pub extern "C" fn c_wrap_drop_my_struct(my_struct_ptr: *const my_struct) {
unsafe {
assert!(!my_struct_ptr.is_null());
drop(Arc::from_raw(my_struct_ptr)); // ref count drops to zero --> object is discarded.
}
}
I'm not sure whether manually incrementing the raw pointer's reference count is safe and idiomatic? Without the increment, the reference count in the getter c_wrap_get_data_from_struct would drop to zero and Rust would destroy the object (which I don't want to happen).
The functions
c_wrap_get_data_from_structandc_wrap_drop_my_structneed to be markedunsafe. Apart from that your code is sound (I think that's what you're asking for, it most definitely is not safe). I would however recommend to useManuallyDropinstead of manually incrementing and decrementing the reference counter. For one that's semantically not what's happening, and for the other that does not require you to think through and prove another set of safety invariants you need to uphold:Another couple of notes:
init_my_structshould be an associated functionMyStruct::init(ornew)#[no_mangle]MyStructc_wrap_drop_my_structasserts the pointer is not null, butc_wrap_get_data_from_structdoesn't