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
usize
also get alo
andhi
method? - Should the methods be named
low
andhigh
instead oflo
andhi
?