Why do overflow checks for `<<` not detect overflows?
An overflow, in the traditional sense, means that the result of an operation cannot be represented within the number of bits allocated to the result. E.g. let res: u8 = 255_u8 + 1_u8
overflows and panic in debug mode with runtime overflow checks.
However, this is not the case for the left shift operator in Rust. E.g. let res: u8 = 255_u8 << 4_u8
will run just fine and produce res == 240
in debug mode with overflow checks.
This behavior is intended and documented.
The following things are considered to be overflow: - When +, * or binary - create a value greater than the maximum value, or less than the minimum value that can be stored. - ... - Using << or >> where the right-hand argument is greater than or equal to the number of bits in the type of the left-hand argument, or is negative.
My question is why the language team decided that 255_u8 << 4_u8
should not be considered an overflow? What's the rationale behind this decision?
In my mind x << y
should be the same as x * 2.pow(y)
, and that is indeed the case with wrapping multiplication (assuming y < bits_of(x)
). E.g. 255_u8.wrapping_mul(1 << 4) == 240_u8
. So the fact that the result of x << y
is not considered when checking overflows seems like an oversight to me.
Motivation: My code had a bug, because I expected u64::checked_shl
to return None
when the result overflows, just like u64::checked_add
and u64::checked_mul
.