Is transmuting to [u8] always sound?

And if no, what could go wrong?

1 Like

Reinterpreting an arbitrary object as a bunch of bytes isn't always sound.

As an example, padding bytes have an undefined value. So if you reinterpret an arbitrary T as bytes, you are promising Rust that all the bytes in the slice are initialized and have a valid bit pattern... Which is a lie (i.e. UB).


You would need to use &[MaybeUninit<u8>] to handle the case where the integers are uninitialized.


If you want to do this, the bytemuck crate provide this functionality, and lists the requirements for a type &T to be safely interpreted as &[u8]


You can also do this without a dependency using std::slice::from_raw_parts. If your type is using the C ABI with #[repr(C)], then you can reasonably assume that its data will have a stable layout. This can be a bit confusing if you're not used to working with arbitrary pointers or data in a language like C or C++, but you can read more about type layouts to get a better idea.

All #[repr(C)] types can have padding, so it'll still be UB if you aren't careful. To remove the padding issue you'd need #[repr(packed)], but that opens up a whole other can of worms.


To be absolutely clear: packed can be very unsafe but there is unfortunately no such keyword for attributes so it just has to be used very very carefully. Which is why it's best avoided if at all possible.