Slice::from_raw_parts disorganizes my data

I'm quite lost with the behavior that I am producing here, and really can't see what I've been doing wrong.

I have a struct:

#[derive(Debug)]
struct MyStruct {
    a: u8,
    b: u8,
    c: u16,
    d: u16,
    e: u16,
}

and I perform some unsafe magic on this (okay, that's already wrong :innocent:):

impl MyStruct {
    fn my_func(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts_mut(
                self as *const _ as *mut u8,
                8, // == mem::size_of::<Self>(), right?
            )
        }
    }
}

I then put random (not overflowing) values in a MyStruct instance, run MyStruct::my_func on a ref to that instance, print the result of this along with the original struct, and I get this:

p: [108, 3, 148, 38, 0, 0, 15, 7]
s: MyStruct { a: 15, b: 7, c: 876, d: 9876, e: 0 }

See this example live here.

Reinterpreting my u16's as two u8's (don't know if having different sizes of integers plays a role here), one can see that no data is lost or corrupted, it's just in a completely different order (always the same):

original | new  byte position
    1    |  7
    2    |  8
    3    |  2
    4    |  1
    5    |  3
    6    |  4
    7    |  5
    8    |  6

Since this order seems quite random however, I feel that I've made something wrong.

The layout of #[repr(Rust)] types (the default) is not specified. Relying on type layouts is UB, in particular, Rust is allowed to reorder fields. You can fix this by specifying a layout.

#[repr(C)]
#[derive(Debug)]
struct MyStruct {
    a: u8,
    b: u8,
    c: u16,
    d: u16,
    e: u16,
}
1 Like

Thanks!

Using #[repr(C)] allows rustc to get it right for the original u8's, and reverts two-by-two the others u8's 'inferred' from the u16's, but at least the 'disorganization pattern' is now much clearer. :sweat_smile:

Do you know if there is any reason for Slice::from_raw_parts not to try to get the u8's 'in order'?

This is because your processor is "little endian". Endianness - Wikipedia

1 Like

This has to do with endian, you are probably on a little endian system (because that's most common), so the byte order for u16 is [lo, hi], so 0xff00 is stored like [0x00, 0xff]

1 Like

Okay, that puts things in order. :thinking:

Didn't thought about so bare bones considerations, thanks a lot!

1 Like

Also, Slice::from_raw_parts only cares about bytes in memory - it doesn't care about the source type, its layout, or its fields whatsoever. Rust put those bytes in memory in a specific way, and from_raw_parts will read them that way. Your best bet would be to manually marshal that structure into bytes with something along the lines of this playground example. Transmuting memory directly to bytes is a very C way of doing things and you'll probably regret using this approach at some point.

You could also look into serde, but I'm not sure if it can do the 'flat' serialization that you are after.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.