How does Rust FFI pass parameters of type `Vec<U8>`?

#[no_mangle]
pub extern fn rust_bytes(s: *const /* What type should be filled here? */) {
    let bytes: Vec<u8> = /// How to write?
}

I collected some information:

https://users.rust-lang.org/t/how-to-return-a-vector-from-c-back-to-rust-ffi/36569/7

https://users.rust-lang.org/t/ffi-how-to-copy-a-vec-u8-into-a-mut-u8/50560

Since the demonstration is incomplete, I still don’t understand.

I tried the code based on the information.

#[no_mangle]
pub extern fn rust_bytes(data: *const c_uchar, data_length: u32) {
    let bytes: Vec<u8> = /// How to read data here and convert it into Vec<u8>?
}

I found the way to pass String:

#[no_mangle]
pub extern fn rust_greeting(to: *const c_char) -> *mut c_char {
    let c_str = unsafe { CStr::from_ptr(to) };
    let recipient = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };

    CString::new("Hello ".to_owned() + recipient).unwrap().into_raw()
}

#[no_mangle]
pub extern fn rust_cstr_free(s: *mut c_char) {
    unsafe {
        if s.is_null() { return }
        CString::from_raw(s)
    };
}

Since FFI can receive strings, it should also be able to receive arrays. But I checked the relevant information, but did not find the relevant API.

Below is the Rust FFI example I collected:

https://github.com/alexcrichton/rust-ffi-examples/

https://github.com/brickpop/flutter-rust-ffi

If there are more examples of FFI, I hope you can tell me, thank you.

slice::from_raw_parts will let you turn those two parameters into a slice reference &[c_uchar], which can then be converted to a Vec<c_uchar> via into() (courtesy of the corresponding From implementation for Vec)

3 Likes

If you are using Rust to expose a C interface, then I recommend you use

In this instance:

use ::safer_ffi::prelude::*;

#[ffi_export]
pub fn rust_bytes (v: repr_c::Vec<u8>) {
    let bytes: Vec<u8> = v.into(); 
}
  • Note: in practice, depending on your use-case (you mention "arrays"), the FFI side may not be able to provide a Vec exactly, but just some general "array" of bytes, i.e., [a reference (view) to a] slice of bytes in Rust parlance. That would lead to this other function signature:

    fn if_no_ffi (bytes: &'_ [u8])
    {
        // use bytes; if ownership is required, then:
        let owned_bytes = bytes.to_vec();
        …
    }
    

    Which can be ffi_export-ed quite easily:

    use ::safer_ffi::prelude::*;
    
    #[ffi_export]
    fn rust_bytes (bytes: c_slice::Ref<'_, u8>)
    {
        let owned_bytes: Vec<u8> = bytes.to_vec();
        …
    }
    
4 Likes

I solved it, thank you.

I posted the complete code:

Rust Code:

use std::os::raw::{c_char, c_uchar};
use core::slice;

#[no_mangle]
pub extern fn rust_bytes(bytes: *const c_uchar, bytes_length: usize) {
    let bytes = unsafe {slice::from_raw_parts(bytes, bytes_length)};
    let mut bytes: Vec<u8> = Vec::from(bytes);
}

Dart Code:

static void rustBytes(String filePath, List<int> bytes) {
    final pData = toPointerUnit8(bytes);
    final ptrResult = _bindings.rust_bytes(pData[0], pData[1]);
    return ptrResult;
  }

  static List toPointerUnit8(List<int> units) {
    final Pointer<Uint8> result = allocate<Uint8>(count: units.length + 1);
    final Uint8List nativeBytes = result.asTypedList(units.length + 1);
    nativeBytes.setAll(0, units);
    nativeBytes[units.length] = 0;
    Pointer<Uint8> data = result.cast();

    int dataLength = units.length;
    return [data, dataLength];
  }

This is amazing. I will study

1 Like