如何将 Rust `Vec<T>` 暴露给 FFI?

Posted

技术标签:

【中文标题】如何将 Rust `Vec<T>` 暴露给 FFI?【英文标题】:How to expose a Rust `Vec<T>` to FFI? 【发布时间】:2017-01-06 14:03:38 【问题描述】:

我正在尝试构造一对元素:

array: *mut T array_len: usize

array 旨在拥有数据

但是,Box::into_raw 将返回 *mut [T]。我找不到任何关于将原始指针转换为切片的信息。它在内存中的布局是什么?我如何从 C 中使用它?我应该转换为*mut T 吗?如果有,怎么做?

【问题讨论】:

【参考方案1】:

如果你只是想让某个 C 函数可变地借用 Vec,你可以这样做:

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);
    

当然,C 函数不应将此指针存储在其他地方,因为这会破坏别名假设。

如果您想将数据的所有权“传递”给 C 代码,您可以这样做:

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);
    

这里,C 函数“取得所有权”,因为我们期望它最终将指针和长度返回给 Rust,例如,通过调用 Rust 函数来释放它:

#[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));

因为Vec::from_raw_parts 需要三个参数,一个指针、一个大小和一个容量,我们要么必须以某种方式跟踪容量,要么在将指针和长度传递给 C 函数之前使用 Vec 的 shrink_to_fit .不过,这可能涉及重新分配。

【讨论】:

这就是我最终使用的:github.com/maidsafe/safe_core/pull/321/…>。除了assert!,我正在考虑使用它,但我没有足够的信心/说服力。【参考方案2】:

您可以通过use [T]::as_mut_ptr 直接从Vec&lt;T&gt;Box&lt;[T]&gt; 或任何其他DerefMut-to-slice 类型获取*mut T 指针。

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);

【讨论】:

一旦vector 被销毁,array 是否仍然有效?这个想法是 array 将拥有数据(我会更新问题)。 @vinipsmaker:不。因此,请防止向量被forgetting 破坏。查看更新。 抱歉,我对解除分配代码在这种方法中的工作方式感到困惑。 into_boxed_slice 将返回...内存中的布局是什么? boxed_slice.as_mut_ptr() 保证返回指向第一个字符的指针?如何转换回 Box&lt;[T]&gt; 以便解除分配? @vinipsmaker: (1) 未指定布局。当前实现使用(ptr, len)。 (2) 也许你应该问一个新问题。但是你可以试试slice::from_raw_parts_mutBox::from_raw,或者使用Vec::from_raw_parts,但是你也需要通过容量。 slice::from_raw_parts + Box::from_raw 在这里会好吗? Box::from_raw 不是获得指向堆栈分配切片的指针而不是原始切片吗?还是Box&lt;[T]&gt;是特例?

以上是关于如何将 Rust `Vec<T>` 暴露给 FFI?的主要内容,如果未能解决你的问题,请参考以下文章

Rust Diesel 原始 SQL 给出错误“`std::result::Result<Vec<T>,diesel::result::Error>` 所需的类型注释”

Rust语言圣经32 - 动态数组Vec

Rust学习教程32 - 动态数组Vec

Rust学习教程32 - 动态数组Vec

Rust学习教程32 - 动态数组Vec

Rust学习教程32 - 动态数组Vec