2

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

  1. Compute the FfiVec
  2. Create a buffer of appropriate size to copy into
  3. 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?

mwlon
  • 798
  • 5
  • 19
  • 1
    Nit: you can replace the code in `Drop` with `_ = Box::from_raw(self.ptr);`. – Chayim Friedman Sep 19 '22 at 03:35
  • 1
    Nit: you can use `copy_from_slice()` in `copy_from_my_vec()`. – Chayim Friedman Sep 19 '22 at 04:42
  • 1
    You could declare `compute_my_vec (len: *mut usize, buf: *mut*u8)` so that it returns the length and buffer. Then either use `malloc` from Rust to allocate the buffer (so that the caller can deallocate it with `free`), or provide a `destroy` function that the user must call to free the buffer. – Jmb Sep 19 '22 at 06:58
  • 1
    @Jmb that makes me think - what if I just return the `v.as_ptr()` along with the length? Then I could avoid passing buffers and copying the data at all, simplifying this a bit. That could be a solution. – mwlon Sep 19 '22 at 12:40
  • I'm having a hard time understanding your use case from the code you gave. Can you add some comments and more explanation for what you're trying to do? Are you trying to pass data from C to Rust, from Rust to C, or both? It looks like Rust declares the length of the buffer, is that correct? – PitaJ Sep 19 '22 at 16:05
  • Are you calling into Rust from C, or into C from Rust? – PitaJ Sep 19 '22 at 16:12
  • 1
    Does this answer your question? [How to expose a Rust \`Vec\` to FFI?](https://stackoverflow.com/questions/39224904/how-to-expose-a-rust-vect-to-ffi) – PitaJ Sep 19 '22 at 16:16

0 Answers0