What is the correct way to extract LSByte from a u32?

Hi,

My code needs to extract LSByte from a u32 (u32 data is from a volatile read of a memory mapped register).

What is the right way to extract the LSByte from a u32 in rust?
What happens internally in rust when a u32 is cast as u8?
will it take the LSByte or MSByte ?
Is it arch dependent?

I did a quick test as below, it extracts the LSByte, but I am not sure this is the correct way and would it work on all hardware independent of endianness, toolchain etc..

fn main() {
  let t:u32 = 0x12345678;
  let b = (t & 0x000000FF) as u8;
  println!("Byte b is {:#x}", b);
}

Byte b is 0x78

If you mean the byte that mathematically has the least place value, then casting to u8 (or casting preceded by masking with 0xff) are guaranteed to be correct.

3 Likes

An alternative to casting to u8, as in @H2CO3's answer, are u32::to_le_bytes() and u32::to_be_bytes():

fn main() {
  let t:u32 = 0x12345678;

  let b = t.to_le_bytes()[0]; // First byte in little-endian order
  // or
  let b = t.to_be_bytes()[3]; // Fourth byte in big-endian order

  println!("Byte b is {:#x}", b);
}

This might make the intent of the code clearer if that memory-mapped register is logically more of a byte-array than an 32-bit integer. In optimized builds both variants in my example generate the same machine code as t as u8 and (t & 0xFF) as u8.

If you want one byte from an u32 in native order instead, you need u32::to_ne_bytes():

fn main() {
  let t:u32 = 0x12345678;

  let b = t.to_ne_bytes()[0]; // First byte in native byte order, that is
                              // LSByte 0x78 on little-endian platforms and
                              // MSByte 0x12 on big-endian platforms

  println!("Byte b is {:#x}", b);
}
1 Like