I'm reading a series of bytes from a socket and I need to put each segment of n bytes as a item in a struct.
use std::mem;
#[derive(Debug)]
struct Things {
x: u8,
y: u16,
}
fn main() {
let array = [22 as u8, 76 as u8, 34 as u8];
let foobar: Things;
unsafe {
foobar = mem::transmute::<[u8; 3], Things>(array);
}
println!("{:?}", foobar);
}
I'm getting errors that say that foobar
is 32 bits when array
is 24 bits. Shouldn't foobar
be 24 bits (8 + 16 = 24)?
The issue here is that the y
field is 16-bit-aligned. So your memory layout is actually
x
padding
y
y
Note that swapping the order of x
and y
doesn't help, because Rust's memory layout for structs is actually undefined (and thus still 32 bits for no reason but simplicity in the compiler). If you depend on it you will get undefined behavior.
The reasons for alignment are explained in Purpose of memory alignment.
You can prevent alignment from happening by adding the attribute repr(packed)
to your struct, but you'll lose performance and the ability to take references of fields:
#[repr(packed)]
struct Things {
x: u8,
y: u16,
}
The best way would be to not use transmute
at all, but to extract the values manually and hope the optimizer makes it fast:
let foobar = Things {
x: array[0],
y: ((array[1] as u16) << 8) | (array[2] as u16),
};
A crate like byteorder may simplify the process of reading different sizes and endianness from the bytes.
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