Safely getting a value out of a packed struct

If one is talking to a C library which has a packed struct .. how does one safely access the values in it? (Assume misaligned non-byte integers in the struct).

The easiest is to define every field as a byte array, as in [u8; 4] for an u32. Accessing a byte array can never be unaligned. You can define getters that convert them:

#[repr(C)]
struct MyStruct {
    my_int: [u8; 4],
}

impl MyStruct {
    fn get_my_int(&self) -> u32 {
        u32::from_ne_bytes(self.my_int)
    }
}
3 Likes

If it's packed (non-standard C extension), define the same struct with #[repr(packed)].

Alternatively, use std::ptr::read_unaligned to read any type at any address.

3 Likes

Correct me if I’m wrong, but I think you’d need to combine it, so #[repr(C, packed)], otherwise e.g. the order of the fields is not specified.

6 Likes

Isn't that way overkill? Rust should do the right thing if you just copy a field value.

(Playground - I examined asm and llvm output)

#[repr(C, packed)]
struct MyStruct {
    my_int: u32,
}

impl MyStruct {
    #[inline(never)]
    pub fn get_my_int(&self) -> u32 {
        self.my_int
    }
}

pub fn g() -> u32 {
    let ms = MyStruct { my_int: 0 };
    ms.get_my_int()
}

Getting something like this

  %0 = bitcast %MyStruct* %self to i32*
  %1 = load i32, i32* %0, align 1
  ret i32 %1

An unaligned load - basically reading from that pointer with align 1, which looks correct.

(inline(never) has no function here - just to make sure it doesn't optimize the llvm output too much)

Straight field access works correctly, but you cannot take a reference to an unaligned field. Specifying them all as byte arrays makes it easier when you do want to take those references.

1 Like

From @alice's simple and yet reliable solution, you can use (or rewrite yourself) a nice wrapper around these "integer-represented-by-an-unaligned-bag-of-bytes" pattern, such as the types from the ::zerocopy::byteorder module

Maybe it's finally redundant now that we can take raw pointers correctly with the addr_of macro

We can take raw pointers without risking of UB, but not read them without read_unaligned().

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.