是否有一种方法来发出一个编译错误,如果一个结构体包含填充?



我真的在一个可选择的可衍生的特性,安全地返回一个对象的唯一表示为字节。在我的应用程序中,我注意到在派生的实现中,通过将哈希作为字节数组进行哈希,速度提高了约20倍。我想,这对于具有良好定义的表示和没有填充Copy类型是安全的。。当前的实现扩展为如下所示:

use core::mem::{size_of, transmute, MaybeUninit};
pub trait ByteValued: Copy {
fn byte_repr(&self) -> &[u8];
}
pub struct AssertByteValued<T: ByteValued> {
_phantom: ::core::marker::PhantomData<T>
}
macro_rules! impl_byte_repr {
() => {
fn byte_repr(&self) -> &[u8] {
let len = size_of::<Self>();
unsafe {
let self_ptr: *const u8 = transmute(self as *const Self);
core::slice::from_raw_parts(self_ptr, len)
}
}
}
}
// Manual implementations for builtin/std types
impl ByteValued for u32 { impl_byte_repr!{} }
impl ByteValued for usize { impl_byte_repr!{} }
impl<T: ByteValued> ByteValued for MaybeUninit<T> { impl_byte_repr!{} }
impl<T: ByteValued, const N: usize> ByteValued for [T; N] { impl_byte_repr!{} }
// Expanded version of a proc_macro generated derived implementation
pub struct ArrayVec<T, const CAP: usize> {
data: [MaybeUninit<T>; CAP],
len: usize,
}
impl<T: Clone, const CAP: usize> Clone for ArrayVec<T, CAP> {
fn clone(&self) -> Self { todo!() }
}
impl<T: Copy, const CAP: usize> Copy for ArrayVec<T, CAP> {}
// This is only valid if all unused capacity is always consistently represented
impl<T: ByteValued, const CAP: usize> ByteValued for ArrayVec<T, CAP> {
fn byte_repr(&self) -> &[u8] {
// Compiletime check all fields are also ByteValued
let _: AssertByteValued<[MaybeUninit<T>; CAP]>;
let _: AssertByteValued<usize>;
// Runtime check for no padding
let _self_size = size_of::<Self>();
let _field_size = size_of::<[MaybeUninit<T>; CAP]>() + size_of::<usize>();
assert!(_self_size == _field_size, "Must not contain padding");
let len = size_of::<Self>();
unsafe {
let self_ptr: *const u8 = transmute(self as *const Self);
::core::slice::from_raw_parts(self_ptr, len)
}
}
}
fn main() {
let x = ArrayVec::<u32, 4> {
data: unsafe { MaybeUninit::zeroed().assume_init() },
len: 0
};
let bytes = x.byte_repr();
assert_eq!(bytes, &[0; 24]);
// This unconditionally panics, but I want a compile error
let y = ArrayVec::<u32, 3> {
data: unsafe { MaybeUninit::zeroed().assume_init() },
len: 0
};
let _ = y.byte_repr();
}

这里的棘手之处在于在byte_repr中断言没有填充。如前所述,这将根据运行时字段的大小之和检查对象大小。我想使断言const得到一个编译错误,但这不会工作,因为它取决于泛型类型。那么,如果结构体在其字段之间包含填充,是否有一种方法可以发出编译错误(可能来自proc_macro) ?

建议从bytemuck::NoUninit开始。这是一个可衍生的trait,它保证该类型没有任何类型的未初始化字节(包括填充)。在实现它之后,您可以使用bytemuck::bytes_of()来获得您想要使用的&[u8]

由于您显式地使用MaybeUninit,因此不能仅为ArrayVec派生,但是您可以添加绑定到ArrayVecT: NoUninit,并为所有NoUninit实现ByteValued,这将检查您关心的T的条件,并简化您需要编写的imps的数量。

相关内容

最新更新