将Rust中的数组返回给FFI

6
我需要编写一个函数,在 Rust 中返回一个 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]>`

1
你的数组在栈上。你必须使用向量或将其装箱,否则会出现生命周期问题。 - Boiethios
@FrenchBoiethios,感谢您的评论。我已更新问题。有什么提示可以帮我解决这个问题吗? - Ernest
2个回答

4

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

#[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
}

1
数组在堆上的内存如何释放? - Sven Döring

3

我正在尝试学习Rust ffi,这些实现是从互联网上的不同来源拼凑而成的。因此,请带着一定的怀疑看待。

目前我有两种方法:

a)从Rust GC中删除数组并返回指针。用户需要承诺稍后调用free。

#[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 回调函数发送数组。用户需要保证不保持引用,但不需要调用 free。
#[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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接