How to expose a Rust `Vec` to FFI?

If you just want some C function to mutably borrow the Vec, you can do it like this:

extern "C" {
    fn some_c_function(ptr: *mut i32, len: ffi::size_t);
}

fn safe_wrapper(a: &mut [i32]) {
    unsafe {
        some_c_function(a.as_mut_ptr(), a.len() as ffi::size_t);
    }
}

Of course, the C function shouldn’t store this pointer somewhere else because that would break aliasing assumptions.

If you want to “pass ownership” of the data to C code, you’d do something like this:

use std::mem;

extern "C" {
    fn c_sink(ptr: *mut i32, len: ffi::size_t);
}

fn sink_wrapper(mut vec: Vec<i32>) {
    vec.shrink_to_fit();
    assert!(vec.len() == vec.capacity());
    let ptr = vec.as_mut_ptr();
    let len = vec.len();
    mem::forget(vec); // prevent deallocation in Rust
                      // The array is still there but no Rust object
                      // feels responsible. We only have ptr/len now
                      // to reach it.
    unsafe {
        c_sink(ptr, len as ffi::size_t);
    }
}

Here, the C function “takes ownership” in the sense that we expect it to eventually return the pointer and length to Rust, for example, by calling a Rust function to deallocate it:

#[no_mangle]
/// This is intended for the C code to call for deallocating the
/// Rust-allocated i32 array.
unsafe extern "C" fn deallocate_rust_buffer(ptr: *mut i32, len: ffi::size_t) {
    let len = len as usize;
    drop(Vec::from_raw_parts(ptr, len, len));
}

Because Vec::from_raw_parts expects three parameters, a pointer, a size and a capacity, we either have to keep track of the capacity as well somehow, or we use Vec’s shrink_to_fit before passing the pointer and length to the C function. This might involve a reallocation, though.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)