将数组分配给 *mut c_void
Assign an array to *mut c_void
我正在为一个库编写绑定,其中我有一个函数,其参数类型为 void*
,在 Rust 中又称为 *mut c_void
。我必须为这个参数分配一个数组,我如何在 Rust 中做到这一点?
我试过转换,transmute
,它不起作用(transmute
说 c_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
,您可能会遇到可移植性问题。例如,在 C
中 int
没有定义的大小,因此您不能盲目地将 &[i32]
转换为原始大小的四倍的 &[u8]
并从中复制字节它到 *mut u8
。这些问题应该非常小心地处理。
我正在为一个库编写绑定,其中我有一个函数,其参数类型为 void*
,在 Rust 中又称为 *mut c_void
。我必须为这个参数分配一个数组,我如何在 Rust 中做到这一点?
我试过转换,transmute
,它不起作用(transmute
说 c_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
,您可能会遇到可移植性问题。例如,在 C
中 int
没有定义的大小,因此您不能盲目地将 &[i32]
转换为原始大小的四倍的 &[u8]
并从中复制字节它到 *mut u8
。这些问题应该非常小心地处理。