将数组分配给 *mut c_void

Assign an array to *mut c_void

我正在为一个库编写绑定,其中我有一个函数,其参数类型为 void*,在 Rust 中又称为 *mut c_void。我必须为这个参数分配一个数组,我如何在 Rust 中做到这一点?

我试过转换,transmute,它不起作用(transmutec_void[u8] 大小不同)。如果重要的话,我会从向量中获取切片。

更新:也许以某种方式使用 vec.as_mut_ptr() 是正确的?

PLAYPEN: http://is.gd/KjgduZ

您描述的 API 看起来很可疑。请记住,在 C 中实际上没有 "arrays" - 数组只是指针的另一个名称,指向连续放置在内存中的多个相同类型的值的开头。因此,在C中不可能只是"assign"一个数组。有两个概念可以理解为"assigning"数组:第一,在某处赋值一个指向数组开头的指针:

const char *s1 = "hello";
const char *s2 = "world";

const char *s = s1;  // make `s` contain a pointer to "hello"
s = s2;  // make `s` contain a pointer to "world"

其次,它是将某些数据从一个指针复制到另一个指针,这通常使用 memcpy() 或类似的东西来完成:

const char *s1 = "hello";

char s2[5];
memcpy(s2, s1, 5);  // copy 5 bytes from the memory pointed at by `s1` to the memory pointed at by `s2`

当我说您的 API 可疑时,您现在可能明白我的意思了。您的回调函数被赋予 void *,但是,没有指示应该使用哪个 "array copy" 方法。

如果是第一个,即将指针复制到一个数组的开头,那么void *类型是极其无益的。它没有说明这个指针应该如何表示。看起来您正在尝试这样做;但是,它不会像您想象的那样工作。这是您代码的编译变体(请注意,它是错误的,很可能会使您的程序崩溃;见下文):

#![feature(libc)]
extern crate libc;

use libc::c_void;

pub extern fn demo(data: *mut *mut c_void) {
    let mut vec = vec!(1, 2, 3);
    unsafe {
        *data = vec.as_mut_ptr() as *mut c_void;
    }
}

(请注意,由于 autoderef,您可以直接在包含向量的 mut 变量上调用 as_mut_ptr()

参数类型现在不只是*mut c_void,而是*mut *mut c_void,即指向*mut c_void的指针。这样调用此函数的程序可以将指向类型 void * 的局部变量的指针传递给此函数并获得指向实际数组的指针,例如

void *data;
some_struct.callback_fn(&data);  // pointer to `demo` is stored in `some_struct`
// data is now whatever your `demo` function has assigned

请注意,您 不能 明智地让 demo 只接受 *mut c_void 因为您唯一可以用它做的就是重新分配参数本身,但重新分配参数将仅重新分配此参数值,即此参数表示的局部变量。在函数之外无法观察到这一点。换句话说,以下代码(也是您提供的代码的变体):

pub extern fn demo(mut data: *mut c_void) {
    let mut vec = vec!(1, 2, 3);
    data = vec.as_mut_ptr() as *mut c_void;
}

什么都不做,Rust 很高兴指出这一点:

<anon>:6:20: 6:28 warning: variable `data` is assigned to, but never used, #[warn(unused_variables)] on by default
<anon>:6 pub extern fn demo(mut data: *mut c_void) {
                            ^~~~~~~~
<anon>:8:5: 8:9 warning: value assigned to `data` is never read, #[warn(unused_assignments)] on by default
<anon>:8     data = vec.as_mut_ptr() as *mut c_void;
             ^~~~

之所以说带*mut *mut c_void的代码是错误的,是因为它实际上违反了内存安全。如果你创建一个 Vec 实例并将它存储到一个局部变量中,当这个变量超出范围时,向量本身将被销毁并且它包装的内存将被释放。因此,使用as_ptr()as_mut_ptr()从中获取的每个指针都将无效。

有几种方法可以解决这个问题,最简单的方法就是 forget() 向量:

use std::mem;

let mut vec = vec![1, 2, 3];
*data = vec.as_mut_ptr() as *mut c_void;
mem::forget(vec);

这样向量就是 "forgotten" - 它的析构函数不会被调用。但是,这种方式会在您的程序中引入内存泄漏。每次调用 demo() 都会分配更多内存但不会释放,因此最终您的程序将使用所有可用内存并可能随后崩溃。然而,在某些上下文中这样做是明智的,尤其是在低级代码中。例如,您的 API 可能指定它只会调用此函数一次。

这种方法的另一个问题是上述方法的逻辑结果。您的 API 可以指定谁应该释放提供给它的指针处的内存。例如,它可能需要传递用 malloc() 分配的内存,然后它会自己用 free() 释放它。或者它可能指定您应该定义另一个函数,该函数将在释放所有分配的内存时调用。这两种方式在 Rust 中实现起来都有些不便;除非这确实是您的情况,否则我不会详细说明如何操作。无论如何,你的 API 必须清楚地指定内存的所有者,你 应该 考虑到这一点,因为 Rust 对所有权更加明确。

另一种可能是您的API要求您将一些数据复制到void *指针指定的内存中。换句话说,它的实现包含这样的代码:

char buffer[256];
some_struct.callback_fn(buffer);

并且它期望在 callback_fn 调用之后 buffer 填充数据。

如果是这种情况,API 自然必须指定您的程序可能使用的缓冲区中的最大字节数,并且您的 demo 函数可能如下所示:

use std::ptr;
use libc::c_void;

pub extern fn demo(data: *mut c_void) {
    let vec: Vec<u8> = vec!(1, 2, 3);
    unsafe { 
        ptr::copy_nonoverlapping(vec.as_ptr(), data as *mut u8, vec.len());
    }
}

(或者,您可以使用 std::slice::from_raw_parts_mut() and use either clone_from_slice() method or bytes::copy_memory() 函数将 data 转换为 &mut [u8],但它们都不稳定,因此不能在稳定的 Rust 上使用)

在这种情况下,您应该特别注意不要溢出调用程序提供给您的缓冲区。其最大大小应在 API.

中指定

另一个问题是复制数组只对字节数组很简单(char * 在 C 端,&[u8]/&mut [u8] 在 Rust 端)。当您开始使用更大的类型时,例如 i32,您可能会遇到可移植性问题。例如,在 Cint 没有定义的大小,因此您不能盲目地将 &[i32] 转换为原始大小的四倍的 &[u8] 并从中复制字节它到 *mut u8。这些问题应该非常小心地处理。