我将复杂的数字数据填入Vec<f64>表格中的外部C库(不想改变),[i_0_real, i_0_imag, i_1_real, i_1_imag, ...]看起来它Vec<f64>的内存布局Vec<num_complex::Complex<f64>>与长度的一半相同,因为num_complex::Complex<f64>数据结构是内存 -布局与此处[f64; 2]记录的兼容.我想这样使用它而不需要重新分配潜在的大缓冲区.
我假设它是有效的使用from_raw_parts()在std::vec::Vec假一新Vec,是以旧的所有权Vec的记忆(由不忘旧Vec),并使用size / 2和capacity / 2,但是这需要不安全的代码.是否有一种"安全"的方式来进行这种数据重新解释?
它Vec在Rust中作为a分配,Vec<f64>并由C函数.as_mut_ptr()填充使用填充的Vec<f64>.
我目前正在编译不安全的实现:
extern crate num_complex;
pub fn convert_to_complex_unsafe(mut buffer: Vec<f64>) -> Vec<num_complex::Complex<f64>> {
let new_vec = unsafe {
Vec::from_raw_parts(
buffer.as_mut_ptr() as *mut num_complex::Complex<f64>,
buffer.len() / 2,
buffer.capacity() / 2,
)
};
std::mem::forget(buffer);
return new_vec;
}
fn main() {
println!(
"Converted vector: {:?}",
convert_to_complex_unsafe(vec![3.0, 4.0, 5.0, 6.0])
);
}
Run Code Online (Sandbox Code Playgroud)
She*_*ter 13
是否有一种"安全"的方式来进行这种数据重新解释?
不.至少,这是因为你需要知道的信息不是在Rust类型系统中表达,而是通过散文(也就是文档)来表达:
Complex<T>是与数组兼容的内存布局[T; 2].
如果a
Vec已经分配了内存,那么它的指针len按顺序指向初始化的连续元素(如果你将它强制转换为切片,你会看到什么),-
Vec文档
数组强制切片(
[T])- 数组文档
由于a Complex与数组存储器兼容,因此数组的数据与切片存储器兼容,并且Vec数据与切片存储器兼容,这种转换应该是安全的,即使编译器无法说明这一点.
此信息应附加(通过注释)到您的不安全区块.
我会对你的函数做一些小的调整:
让两个Vecs同时指向相同的数据让我非常紧张.通过引入一些变量并在创建另一个变量之前忘记一个变量,可以简单地避免这种情况.
删除return关键字更加惯用
添加一些断言,数据的起始长度是2的倍数.
正如罗德里戈指出的那样,容量可能很容易变成奇数.为了避免这种情况,我们打电话shrink_to_fit.这具有Vec 可能需要重新分配和复制存储器的缺点,这取决于实现.
展开该unsafe块以涵盖确保维护安全不变量所需的所有相关代码.
pub fn convert_to_complex(mut buffer: Vec<f64>) -> Vec<num_complex::Complex<f64>> {
// This is where I'd put the rationale for why this `unsafe` block
// upholds the guarantees that I must ensure. Too bad I
// copy-and-pasted from Stack Overflow without reading this comment!
unsafe {
buffer.shrink_to_fit();
let ptr = buffer.as_mut_ptr() as *mut num_complex::Complex<f64>;
let len = buffer.len();
let cap = buffer.capacity();
assert!(len % 2 == 0);
assert!(cap % 2 == 0);
std::mem::forget(buffer);
Vec::from_raw_parts(ptr, len / 2, cap / 2)
}
}
Run Code Online (Sandbox Code Playgroud)
为了避免所有对容量的担忧,您可以将切片转换为Vec.这也没有任何额外的内存分配.它更简单,因为我们可以"丢失"任何奇怪的尾随值,因为它Vec仍然保持它们.
pub fn convert_to_complex(buffer: &[f64]) -> &[num_complex::Complex<f64>] {
// This is where I'd put the rationale for why this `unsafe` block
// upholds the guarantees that I must ensure. Too bad I
// copy-and-pasted from Stack Overflow without reading this comment!
unsafe {
let ptr = buffer.as_ptr() as *mut num_complex::Complex<f64>;
let len = buffer.len();
std::slice::from_raw_parts(ptr, len / 2)
}
}
Run Code Online (Sandbox Code Playgroud)