Integer Overflow

Integer overflow happens when arithmetic produces a value outside an integer type’s range; Rust checks it in debug builds and offers explicit methods for every intended overflow policy.

What it is

Each integer type has a finite range. For example, u8 stores 0..=255, while i8 stores -128..=127. Arithmetic that exceeds the range is overflow.

The Book teaches that debug builds check integer overflow and panic when it occurs. Release builds do not include those checks by default and use two’s-complement wrapping for primitive integer operations. Relying on accidental wrapping is considered an error.

Rust gives named arithmetic methods so the overflow policy is visible in code: checked_*, wrapping_*, saturating_*, and overflowing_*.

How it works

checked_add and related methods return Option<T>, using None for overflow. wrapping_add wraps modulo the integer range in all build modes. saturating_add clamps at the minimum or maximum. overflowing_add returns both the wrapped value and a boolean overflow flag.

Choose the method that matches the domain. Counters that must never exceed a maximum may saturate. Cryptographic or hash-style arithmetic may wrap deliberately. User-facing calculations often need checked arithmetic and error handling.

The compile-time range also affects constants: a literal or const expression that cannot fit the annotated type is rejected rather than becoming a surprising runtime value.

Example

fn main() {
    let max = u8::MAX;
 
    assert_eq!(max.checked_add(1), None);
    assert_eq!(max.wrapping_add(1), 0);
    assert_eq!(max.saturating_add(1), u8::MAX);
 
    let (value, overflowed) = max.overflowing_add(1);
    assert_eq!(value, 0);
    assert!(overflowed);
}

Common errors

Overflow in constants is rejected at compile time:

// const BAD: u8 = 255 + 1;

Typical diagnostic:

error[E0080]: attempt to compute `u8::MAX + 1_u8`, which would overflow

Fix by choosing a wider type or spelling the intended policy:

const WRAPPED: u8 = u8::MAX.wrapping_add(1);
const CLAMPED: u8 = u8::MAX.saturating_add(1);

Best practice

  • ✅ Use plain +, -, and * only when overflow would be a bug and tests should catch it.
  • ✅ Use checked_* when overflow is input-dependent and must be reported or handled.
  • ✅ Use wrapping_* only when modular arithmetic is the actual algorithm.
  • ✅ Use saturating_* for counters, scores, capacities, and limits that should clamp.

Pitfalls

  • ⚠️ Depending on debug panics as production validation; release behavior differs unless overflow checks are configured.
  • ⚠️ Treating wrapping_* as a quick fix instead of documenting why wrapping is correct; see Relying on Integer Overflow.
  • ⚠️ Forgetting integer division truncates toward zero.
  • ⚠️ Parsing into a smaller integer type without deciding how out-of-range input should fail.

See also

Scalar Types · Relying on Integer Overflow · panic! · Result · Constants · Testing · Type Inference · Arrays · Basic Concepts & Syntax

Sources