Preparing an array of structs for FFI

Rust slices expose both a pointer to the first element (if any), and a len getter.
These two elements are supposed to be used together for FFI, by letting you manipulate "C slices" (thinggy * pointer + size_t count).

You can create a convenience struct to hold both fields, as would be done in C, but one thing to be careful with is ownership: since a Vec is allocated by Rust, you will need to give the owned slice back to Rust so that it can properly free it:

use ::core::{ptr, slice};

#[repr(C)]
pub
struct FFIBoxedSlice {
    ptr: *mut SerialPortInfoFFI,
    len: usize, // number of elems
}

// Helper (internal) function
fn vec_to_ffi (v: Vec<SerialPortInfoFFI>)
  -> FFIBoxedSlice
{
    // Going from Vec<_> to Box<[_]> just drops the (extra) `capacity`
    let boxed_slice: Box<[SerialPortInfoFFI]> = v.into_boxed_slice();
    let len = boxed_slice.len();
    let fat_ptr: *mut [SerialPortInfoFFI] =
        Box::into_raw(boxed_slice)
    ;
    let slim_ptr: *mut SerialPortInfoFFI = fat_ptr as _;
    FFIBoxedSlice { ptr: slim_ptr, len }
}

#[no_mangle]
pub
unsafe
extern "C"
fn free_boxed_slice (FFIBoxedSlice { ptr, len }: FFIBoxedSlice)
{
    if ptr.is_null() {
        eprintln!("free_boxed_slice() errored: got NULL ptr!");
        ::std::process::abort();
    }
    let slice: &mut [SerialPortInfoFFI] =
        slice::from_raw_parts_mut(ptr, len)
    ;
    drop(Box::from_raw(slice));
}
1 Like