如何将固定大小的字节数组类型转换为结构体



我想在不做任何工作的情况下,将堆栈分配的字节数组重新解释为堆栈分配的(静态保证的(结构——只是告诉编译器"是的,我保证它们都是一样大小的,什么都可以;。我该怎么做?

我尝试过transmute,但它无法编译。

fn from_u8_fixed_size_array<T>(arr: [u8; size_of::<T>()]) -> T {
unsafe { mem::transmute(arr) }
}
cannot transmute between types of different sizes, or dependently-sized types E0512 
Note: source type: `[u8; _]` (this type does not have a fixed size) 
Note: target type: `T` (this type does not have a fixed size)

还有这种函数的变体,可以编译,但它要求TCopy:

fn from_u8_fixed_size_array(arr: [u8; size_of::<T>()]) -> T {        
unsafe { *(&arr as *const [u8; size_of::<T>()] as *const T) }
}

使用Rust 1.64时,我在[u8; size_of::<T>()]上出现编译错误(无法使用T执行const操作(。我尝试使用const泛型参数,但问题仍然存在(我无法引入where子句来约束此常量以匹配size_of::<T>()(。

由于数组是按值传递的,结果是一个值,因此必须复制一些字节;这意味着一种CCD_ 7。我建议使用切片而不是数组,并在运行时检查大小。

如果你已经准备好处理未定义的行为,你可能会考虑第二个版本,它不复制任何东西:它只是按原样重新解释存储。我不确定我会那样做,但是。。。

编辑

原始代码是使用夜间和一个特定功能编译的。我们可以简单地使用transmute_copy()按值获取数组并发出值。

而且,我认为函数本身应该用unsafe来限定,而不仅仅是它们的一些操作,因为没有什么可以(静态地(保证这些转换是正确的。

#![feature(generic_const_exprs)] // nightly required
unsafe fn from_u8_slice_v1<T>(arr: &[u8]) -> T {
let mut result = std::mem::MaybeUninit::<T>::uninit();
let src = &arr[0] as *const u8;
let dst = result.as_mut_ptr() as *mut u8;
let count = std::mem::size_of::<T>();
assert_eq!(count, arr.len());
std::ptr::copy_nonoverlapping(src, dst, count);
result.assume_init()
}
unsafe fn from_u8_slice_v2<T>(arr: &[u8]) -> &T {
let size = std::mem::size_of::<T>();
let align = std::mem::align_of::<T>();
assert_eq!(size, arr.len());
let addr = &arr[0] as *const _ as usize;
assert_eq!(addr % align, 0);
&*(addr as *const T) // probably UB
}
unsafe fn from_u8_fixed_size_array<T>(
arr: [u8; std::mem::size_of::<T>()]
) -> T {
std::mem::transmute_copy(&arr)
}
fn main() {
let a = [1, 2];
println!("{:?}", a);
let i1 = unsafe { from_u8_slice_v1::<i16>(&a) };
println!("{:?}", i1);
let i2 = unsafe { from_u8_slice_v2::<i16>(&a) };
println!("{:?}", i2);
let i3 = unsafe { from_u8_fixed_size_array::<i16>(a) };
println!("{:?}", i3);
}
/*
[1, 2]
513
513
513
*/

最新更新