For most operators that might overflow, Rust provides a checked version. For example, to test if an addition overflows one could use checked_add
:
match 255u8.checked_add(1) {
Some(_) => println!("no overflow"),
None => println!("overflow!"),
}
This prints "overflow!"
. There is also a checked_shl
, but according to the documentation it only checks if the shift is larger than or equal to the number of bits in self
. That means that while this:
match 255u8.checked_shl(8) {
Some(val) => println!("{}", val),
None => println!("overflow!"),
}
is caught and prints "overflow!"
, This:
match 255u8.checked_shl(7) {
Some(val) => println!("{}", val),
None => println!("overflow!"),
}
simply prints 128
, clearly not catching the overflow.
What is the correct way to check for any kind of overflow when shifting left?
I'm not aware of any idiomatic way of doing this, but something like implementing your own trait would work: Playground
The algorithm is basically to check if there are not fewer leading zeros in the number than the shift size
trait LossCheckedShift {
fn loss_checked_shl(self, rhs: u32) -> Option<Self>
where Self: std::marker::Sized;
}
impl LossCheckedShift for u8 {
fn loss_checked_shl(self, rhs: u32) -> Option<Self> {
(rhs <= self.leading_zeros()).then_some(self << rhs)
}
}
fn main() {
match 255u8.loss_checked_shl(7) {
Some(val) => println!("{}", val),
None => println!("overflow!"), // <--
}
match 127u8.loss_checked_shl(1) {
Some(val) => println!("{}", val), // <--
None => println!("overflow!"),
}
match 127u8.loss_checked_shl(2) {
Some(val) => println!("{}", val),
None => println!("overflow!"), // <--
}
}