将数组从 Rust 返回到 FFI

Returning array from Rust to FFI

我需要在 Rust 中编写一个 returns u16 整数数组的函数。然后这个函数应该被FFI使用。

extern crate libc;
use libc::{uint16_t};

#[no_mangle]
pub extern fn ffi_test() ->  *const uint16_t {
    let test: [u16;4] = [1,2,3,4];

    test.as_ptr()
}

Rust 代码编译没有错误。我使用 Ruby 来测试 ffi 调用:

# coding: utf-8
require 'ffi'

module MyMod
  extend FFI::Library
  ffi_lib 'my_ffi_test_lib'
  attach_function :ffi_test, [], :pointer
end

a_ptr = MyMod.ffi_test
size = 4
result_array = a_ptr.read_array_of_uint16(size)
p result_array

但结果完全错误(预期:[1, 2, 3, 4]):

$ ruby ffi_test.rb
[57871, 25191, 32767, 0]

好像我正在读取完全不同的内存地址。我想也许我不应该在 Rust 数组上使用 #as_ptr()

编辑

根据@FrenchBoiethios 的建议,我尝试将数组装箱:

extern crate libc;
use libc::{uint16_t};

#[no_mangle]
pub extern fn ffi_test() ->  *mut uint16_t {
    let test: [u16;4] = [1,2,3,4];

    let b = Box::new(test);
    Box::into_raw(b)
}

这给出了编译错误:

note: expected type `std::boxed::Box<u16>`
         found type `std::boxed::Box<[u16; 4]>`

你的数组在堆栈上,所以当你 returns 它作为指针(返回的指向局部变量的指针)时存在生命周期问题。您必须在堆中分配它:

#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
    let mut test = vec![1, 2, 3, 4];
    let ptr = test.as_mut_ptr();
    
    std::mem::forget(test); // so that it is not destructed at the end of the scope
    
    ptr
}

#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
    let test = Box::new([1u16, 2, 3, 4]);  // type must be explicit here...

    Box::into_raw(test) as *mut _          // ... because this cast can convert
                                           // *mut [i32; 4] to *mut u16
}

我正在尝试学习 Rust ffi,这些实现是来自互联网上不同来源的科学怪人创作。因此,请对它持保留态度。

目前我有两种方法:

a) 从 rust GC 和 return 点中移除数组。用户需要承诺以后免费拨打。

#[repr(C)]
pub struct V2 {
    pub x: i32,
    pub y: i32,
}

#[repr(C)]
struct Buffer {
    len: i32,
    data: *mut V2,
}

#[no_mangle]
extern "C" fn generate_data() -> Buffer {
    let mut buf = vec![V2 { x: 1, y: 0 }, V2 { x: 2, y: 0}].into_boxed_slice();
    let data = buf.as_mut_ptr();
    let len = buf.len() as i32;
    std::mem::forget(buf);
    Buffer { len, data }
}

#[no_mangle]
extern "C" fn free_buf(buf: Buffer) {
    let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len as usize) };
    let s = s.as_mut_ptr();
    unsafe {
        Box::from_raw(s);
    }
}

b) 通过FFI回调函数发送数组。用户需要承诺不保留引用,但不需要免费拨打。

#[no_mangle]
pub extern "C" fn context_get_byte_responses(callback: extern "stdcall" fn (*mut u8, i32)) -> bool {
    let bytes: Vec<u8> = vec![];
    callback(bytes.as_mut_ptr(), bytes.len() as i32);
    true
}