我想为 C 代码实现一个 FFI,它调用用 Rust 编写的函数并将结果存储在 Rust 对象中以供以后使用。
Arc<T>
,其中 T
等于存储日期的 Rust 对象的类型。Arc<T>
转换为原始指针 *const T
并将该指针传递给 C
我了解到,由于 ABI 兼容性,C 可以将 *const T
解释为 *void
指针。
我关心的是 Arc 及其在 C 中引用的对象的惯用和内存安全处理。到目前为止,我已经想出了这个
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.
}
}
我不确定手动增加原始指针的引用计数是否安全且惯用?如果没有增量,getter
c_wrap_get_data_from_struct
中的引用计数将降至零,Rust 将销毁该对象(我不希望发生这种情况)。
功能
c_wrap_get_data_from_struct
和c_wrap_drop_my_struct
需要标记为unsafe
。除此之外,您的代码是健全的(我认为这就是您所要求的,它绝对不安全)。不过,我建议使用 ManuallyDrop
而不是手动递增和递减参考计数器。对于一个在语义上不是正在发生的事情的情况,对于另一个不需要您思考并证明您需要维护的另一组安全不变量的情况:
use std::sync::Arc;
use std::mem::ManuallyDrop;
#[no_mangle]
pub unsafe extern "C" fn c_wrap_get_data_from_struct(my_struct_ptr: *const MyStruct) -> i32 {
// reconstruct the Rust object to extract a value
// wrap it in `ManuallyDrop` because we do not want to consume the `Arc` (decrement the reference count).
let mys = ManuallyDrop::new(unsafe { Arc::from_raw(my_struct_ptr) });
mys.value // extract and return value
}
另外一些注意事项:
init_my_struct
应该是关联函数 MyStruct::init
(或 new
)#[no_mangle]
MyStruct
c_wrap_drop_my_struct
断言指针不为空,但 c_wrap_get_data_from_struct
不