Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose a Rust `Vec<T>` to FFI?

Tags:

rust

ffi

I'm trying to construct a pair of elements:

  • array: *mut T
  • array_len: usize

array is intended to own the data

However, Box::into_raw will return *mut [T]. I cannot find any info on converting raw pointers to slices. What is its layout in memory? How do I use it from C? Should I convert to *mut T? If so, how?

like image 646
vinipsmaker Avatar asked Aug 30 '16 10:08

vinipsmaker


People also ask

How do you clear a vector in Rust?

To remove all elements from a vector in Rust, use . retain() method to keep all elements the do not match. let mut v = vec![

How do you initialize a VEC in Rust?

In Rust, there are several ways to initialize a vector. In order to initialize a vector via the new() method call, we use the double colon operator: let mut vec = Vec::new();

What is VEC rust?

Vector is a module in Rust that provides the container space to store values. It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity.


Video Answer


2 Answers

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.

like image 92
sellibitze Avatar answered Oct 01 '22 19:10

sellibitze


You could use [T]::as_mut_ptr to obtain the *mut T pointer directly from Vec<T>, Box<[T]> or any other DerefMut-to-slice types.

use std::mem;  let mut boxed_slice: Box<[T]> = vector.into_boxed_slice();  let array: *mut T = boxed_slice.as_mut_ptr(); let array_len: usize = boxed_slice.len();  // Prevent the slice from being destroyed (Leak the memory). mem::forget(boxed_slice); 
like image 45
kennytm Avatar answered Oct 01 '22 18:10

kennytm