`u32::from_ne_bytes` is transmuting unaligned data, how is this safe?

I read that transmuting any random [u8] to anything, including u32, is not safe. That's because the [u8] may not be aligned properly, and u32 pointers need to be aligned to 4 bytes.

Browsing the source code for u32, I noticed the following transmute:

pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
    // SAFETY: integers are plain old datatypes so we can always transmute to them
    unsafe { mem::transmute(bytes) }
}

So is casting a byte array to a plain old data type okay even if the byte array is not aligned to anything? Or is it safe because we are not actually transmuting the pointer but the data itself?

1 Like

See the discussion at the top of this documentation.

2 Likes

You've misunderstood what this does. It's transmuting an array (of type [u8; std::mem::size_of::<Self>()] aka [u8; 4]) not a pointer.

Transmuting the value is fine. What's not okay is transmuting the pointer since that need not be aligned adequately. Reading from a misaligned pointer is UB (unless you use the correct ptr operations, but those are slow).

4 Likes

Does this mean that for example it is also safe to transmute a [u8; 8] to a [u32;2]?

Yes, it's safe to transmute a [u8; 8] to a [u32;2], however it is not safe to transmute a &[u8; 8] to a &[u32;2], because of alignment. Note how the introduction of references made alignment matter. Alignment only matters for pointers.

5 Likes

Terminology nitpick: It's definitely not safe -- it'll still need unsafe. But yes, it's sound.

4 Likes

Um.. Should one of those be the other way around?

1 Like

Nope, it's correct as written

1 Like

Oh, got it. References. That makes sense.

2 Likes

Thanks for your answers :slight_smile:

Note that this is highly chip dependent, not even just target-dependent. See Data alignment for speed: myth or reality? – Daniel Lemire's blog for some numbers.

Based on the description of the MISALIGNED_LOADS counter in uProf, new Zen3 processors don't even bother reporting something as misaligned unless it crosses the 64 B cache line boundary or the 4 KiB page boundary.

But of course that's a big fat new desktop chip. The situation can be quite different on older or smaller chips.

And you do still need to use the correct operations to read & write unaligned things. Misaligned references are UB in Rust, no matter what your CPU does.

7 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.