Summary
Add two methods, .lo() and .hi(), to all unsigned integer types except u8 and usize, which return the most (hi) or least (lo) significant half of an integer.
Motivation
Low-level code that interacts closely with hardware often needs to manipulate integers in specific ways. One of the most common operations is getting the low or high half of an integer. Currently, this can be accomplished by shifting and converting (masking) the integer:
let my_int: u32 = 0xABCDEF55;
let high: u16 = (my_int >> 16) as u16;
let low: u16 = my_int as u16;
To make this common operation shorter and easier to use, we can instead provide two simple methods on integers which do the same thing:
let my_int: u32 = 0xABCDEF55;
let high: u16 = my_int.hi();
let low: u16 = my_int.lo();
Detailed design
Two methods are added to u16, u32, u64 and u128. u8 is omitted because it can’t be split into smaller integers. usize is omitted because its size isn’t uniform on all architectures, so the result of the split would also be different on each architecture, possibly causing Rust code to compile on one architecture, but fail to compile on another.
The implementation in libcore could look like this:
impl u16 {
fn lo(&self) -> u8 { *self as u8 }
fn hi(&self) -> u8 { (*self >> 8) as u8 }
}
impl u32 {
fn lo(&self) -> u16 { *self as u16 }
fn hi(&self) -> u16 { (*self >> 16) as u16 }
}
impl u64 {
fn lo(&self) -> u32 { *self as u32 }
fn hi(&self) -> u32 { (*self >> 32) as u32 }
}
impl u128 {
fn lo(&self) -> u64 { *self as u64 }
fn hi(&self) -> u64 { (*self >> 64) as u64 }
}
Here is an implementation on the Rust playground using a LoHi trait instead of an inherent impl.
Drawbacks
The methods add a bit of API surface to core, which needs to be maintained and documented.
Alternatives
- Continue the bit-fiddling like before
Unresolved questions
- Should signed integers implement the methods, too?
- Should
usizealso get aloandhimethod? - Should the methods be named
lowandhighinstead ofloandhi?
Thanks for mentioning it.