Rust / Dealing with Integer Overflow
Overflow can happen when doing integer arithmetics in Rust, for example:
let _ = 255u8 + 1;
let _ = 0u8 - 1;
Rust handle overflow differently between Debug and Release mode:
- In debug mode, Rust includes checks for integer overflow that will panic at runtime.
- In release mode, there is no integer overflow check, so, no panic. Instead, Rust will perform two’s complement wrapping, for the
u8
type, the value 256 will be wrapped into 0, the value 257 will be wrapped into 1, while the value -1 will be wrapped into 255, and so on. Hence, be mindful that the values you get might not be the ones you expected. Or you can explicitly enable overflow check by addingoverflow-checks = true
to yourCargo.toml
.
To deal with integer overflow, Rust provides some built-in functions for integer types so you can explicitly control the overflow behavior:
Wrapping the values with
wrapping_*
methods, likewrapping_add
:u8::MAX.wrapping_add(2) == 1u8 0u8.wrapping_sub(1) == u8::MAX
Returning a wrapped value, and an overflow indicator with
overflowing_*
methods:5u8.overflowing_add(1) == (6u8, false) u8::MAX.overflowing_add(1) == (0u8, true)
Check and return
None
if there is an overflow withchecked_*
methods:5u8.checked_add(1) == Some(6u8) u8::MAX.checked_add(1) == None
Saturate at the maximum or minimum value of the type with
saturating_*
methods:u8::MAX.saturating_add(5) == u8::MAX 0u8.saturating_sub(5) == 0u8
To reduce boilerplates, Rust provides Wrapping<T>
◹ and Saturating<T>
◹ types to automatically provide the desired overflow behavior for your types:
use std::num::Saturating;
let a = Saturating(u32::MAX);
(a + 1).0 == u32::MAX
When casting between types, wrapping is the default behavior:
(257u32 as u8) == 1u8
References:
- https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md◹
- https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/◹