Is it possible to bound the type parameter T here with the constraint size_of::<T>() == 4, so that this function compiles (and so that any usage site of foo fails if the provided type is in fact not size 4?
// I want this to compile
fn foo<T>(t: T) where T: Sized {
unsafe { std::mem::transmute::<T, [u8; 4]>(t); };
}
// and this not to compile
fn bar() {
let x = [0u8; 5];
foo(x);
}
You can make compilation fail for any call with size of non-4, but this will not make the transmute work. You need to use transmute_copy():
fn foo<T>(t: T) {
struct AssertSize4<T>(T);
impl<T> AssertSize4<T> {
const ASSERT: () = if std::mem::size_of::<T>() != 4 {
panic!("size is not 4 bytes");
};
}
let _ = AssertSize4::<T>::ASSERT;
let t = std::mem::ManuallyDrop::new(t);
unsafe {
std::mem::transmute_copy::<T, [u8; 4]>(&*t);
};
}
Beware that this will generate a post-monormphization error, meaning it'll succeed cargo check but fail cargo build.
However, your code is still incorrect: the size of something being 4 bytes doesn't mean it can be freely converted to 4 bytes. Take this struct, for example:
#[repr(C)]
struct Evil {
_a: u8,
_b: u16,
}
The size of this struct is four bytes, but passing it to foo() will invoke Undefined Behavior. This is because it has padding bytes, which are equivalent to uninitialized bytes (another example of unitialized memory is an uninitialized MaybeUninit). Transmuting uninitialized bytes to any type that does not allow uninitialized bytes, and that includes integers such as u8, is immediate Undefined Behavior.
One possibility is to transmute the data into [MaybeUninit<u8>; 4]. But you may never assume this bytes are initialized, by doing operations such as assume_init() on them.
Another operation is to make sure the bytes are initialized. So, how do you accept only types with only initialized bytes?
You cannot. Your only chance is creating an unsafe trait OnlyInitialized and unsafely impl it for types. You might want to also provide a derive macro for it (but the checks are subtle! don't assume you can just think of all of them). At this point, just use bytemuck.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With