I have a Rust library that returns Vecs of size that cannot be predicted by the caller, and I'm looking for an elegant way to implement the FFI interface.
Since Vecs can't be passed over FFI, I understand I need to first return the length, and then fill a buffer with the contents.
Here's my current solution, which works by manually managing memory of the Vec and trusting the caller to
- Compute the
FfiVec - Create a buffer of appropriate size to copy into
- Copy from the
FfiVec, cleaning it up in the process
#[repr(C)]
pub struct FfiVec<T: Sized> {
ptr: *mut Vec<T>,
len: c_uint, // necessary for C process to know length of Rust Vec
}
impl<T: Sized> FfiVec<T> {
pub fn from_vec(v: Vec<T>) -> Self {
let len = v.len() as c_uint;
FfiVec {
ptr: Box::into_raw(Box::new(v)),
len,
}
}
pub fn drop(self) {
unsafe {
std::ptr::drop_in_place(self.ptr);
std::alloc::dealloc(self.ptr as *mut u8, Layout::new::<Vec<T>>())
}
}
}
#[no_mangle]
pub extern compute_my_vec() -> FfiVec<u8> {...}
#[no_mangle]
pub extern copy_from_my_vec(ffi_vec: FfiVec<u8>, buffer: *mut u8) -> {
let len = c_vec.len as usize;
let buffer_slice = unsafe { std::slice::from_raw_parts_mut(buffer, len) };
for (i, &item) in c_vec.slice().iter().take(len).enumerate() {
buffer_slice[i] = item;
}
c_vec.drop()
}
Is there a simpler way to do this? Or a common library that can do this for me?