Case-by-case behavior
overflow-checks
is a blunt tool: it's a global setting that affects the whole program.
It often happens that you want to handle integer overflows differently depending on the context: sometimes
wrapping is the right choice, other times panicking is preferable.
wrapping_
methods
You can opt into wrapping arithmetic on a per-operation basis by using the wrapping_
methods1.
For example, you can use wrapping_add
to add two integers with wrapping:
#![allow(unused)] fn main() { let x = 255u8; let y = 1u8; let sum = x.wrapping_add(y); assert_eq!(sum, 0); }
saturating_
methods
Alternatively, you can opt into saturating arithmetic by using the saturating_
methods.
Instead of wrapping around, saturating arithmetic will return the maximum or minimum value for the integer type.
For example:
#![allow(unused)] fn main() { let x = 255u8; let y = 1u8; let sum = x.saturating_add(y); assert_eq!(sum, 255); }
Since 255 + 1
is 256
, which is bigger than u8::MAX
, the result is u8::MAX
(255).
The opposite happens for underflows: 0 - 1
is -1
, which is smaller than u8::MIN
, so the result is u8::MIN
(0).
You can't get saturating arithmetic via the overflow-checks
profile setting—you have to explicitly opt into it
when performing the arithmetic operation.
You can think of methods as functions that are "attached" to a specific type. We'll cover methods (and how to define them) in the next chapter.
Exercise
The exercise for this section is located in 02_basic_calculator/09_saturating