【问题标题】:Returning array from Rust to FFI将数组从 Rust 返回到 FFI
【发布时间】:2019-12-28 04:26:16
【问题描述】:

我需要在 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]>`

【问题讨论】:

  • 你的数组在栈上。您必须使用矢量或框,否则,您将遇到终身问题。
  • @FrenchBoiethios 感谢您的评论。我更新了问题。任何提示我该如何解决这个问题?

标签: rust ffi


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    我正在尝试学习 Rust ffi,这些实现是来自互联网不同来源的科学怪人创造。所以带着一粒盐吃。

    目前我有两种方法:

    a) 从 rust GC 中删除数组并返回该点。用户需要承诺以后免费通话。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-31
      • 2017-07-18
      • 1970-01-01
      • 2019-06-15
      • 1970-01-01
      • 1970-01-01
      • 2017-01-20
      • 1970-01-01
      相关资源
      最近更新 更多