RustArena

Rust integer types and integer overflow

i8 to i128, u8 to usize: Rust integer ranges, what happens on overflow in debug and release builds, and checked, wrapping and saturating math.

Rust has signed (i) and unsigned (u) integers of fixed sizes. The number is how many bits they use:

Type Range Typical use
u8 0 to 255 bytes, small counts
i32 about -2.1 billion to 2.1 billion the default for integer literals
u32 0 to about 4.3 billion counts, ids
i64 / u64 about ±9.2 × 10^18 / 0 to 1.8 × 10^19 money in cents, timestamps, big totals
i128 / u128 huge rare: cryptography, very large math
usize / isize pointer-sized (64-bit on most machines) indexes and lengths: v.len() is a usize

Each type has MIN and MAX constants, like u8::MAX (255) and i32::MAX (2147483647). Literals can use underscores for readability (1_000_000), other bases (0xff, 0b1010, 0o17) and a type suffix (255u8, 10_i64).

What happens on overflow

Overflow is a result that doesn't fit in the type: 250u8 + 10 would be 260, but a u8 stops at 255. What Rust does depends on the build:

Either way it's never undefined behavior like in C. When the compiler can see the overflow, it refuses to build:

fn main() {
    let level: u8 = 256;
    println!("{level}");
}
error: literal out of range for `u8`

Choosing what overflow means

When overflow is possible, say what you want with explicit methods:

fn main() {
    let hp: u8 = 250;
    println!("{:?}", hp.checked_add(10));
    println!("{:?}", hp.checked_add(5));
    println!("{}", hp.wrapping_add(10));
    println!("{}", hp.saturating_add(10));
    println!("{:?}", hp.overflowing_add(10));
}
None
Some(255)
4
255
(4, true)

Common mistakes

Example

fn main() {
    println!("u8: {} to {}", u8::MIN, u8::MAX);
    println!("i32: {} to {}", i32::MIN, i32::MAX);
    println!("big literal: {}", 1_000_000_u64 * 1_000_000);
    println!("hex 0xff = {}, binary 0b1010 = {}", 0xff, 0b1010);

    let stock: u8 = 250;
    match stock.checked_add(10) {
        Some(n) => println!("new stock {n}"),
        None => println!("stock would overflow a u8"),
    }
    println!("health capped at {}", stock.saturating_add(10));

    let empty: Vec<i32> = Vec::new();
    println!("last index of empty list: {:?}", empty.len().checked_sub(1));
}

Output:

u8: 0 to 255
i32: -2147483648 to 2147483647
big literal: 1000000000000
hex 0xff = 255, binary 0b1010 = 10
stock would overflow a u8
health capped at 255
last index of empty list: None

Practice it