Search code examples
rustrust-cargointeger-overflow

Why does Rust perform integer overflow checks in --release?


I have this piece of simple code:

let val: u8 = 255 + 1;
println!("{}", val);

It is said here that such a code will compile normally if run with the --release flag.

I am running this code via cargo run --release, and I still see the checks:

error: this arithmetic operation will overflow
 --> src/main.rs:2:19
  |
2 |     let val: u8 = 255 + 1;
  |                   ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
  |
  = note: `#[deny(arithmetic_overflow)]` on by default

error: could not compile `rust-bin` due to previous error

Am I missing something?


Solution

  • The book is slightly imprecise. Overflow is disallowed in both debug and release modes, it's just that release mode omits runtime checks for performance reasons (replacing them with overflow, which CPUs typically do anyway). Static checks are not removed because they don't compromise on performance of generated code. This prints 0 in release mode and panics in debug1:

    let x: u8 = "255".parse().unwrap();
    let val: u8 = x + 1;
    println!("{}", val);
    

    You can disable the compile-time checks using #[allow(arithmetic_overflow)]. This also prints 0 in release mode and panics in debug:

    #[allow(arithmetic_overflow)]
    let val: u8 = 255 + 1;
    println!("{}", val);
    

    The correct approach is to not depend on this behavior of release mode, but to tell the compiler what you want. This prints 0 in both debug and release mode:

    let val: u8 = 255u8.wrapping_add(1);
    println!("{}", val);
    

    1 The example uses "255".parse() because, to my surprise, let x = 255u8; let val = x + 1; doesn't compile - in other words, rustc doesn't just prevent overflow in constant arithmetic, but also wherever else it can prove it happens. The change was apparently made in Rust 1.45, because it compiled in Rust 1.44 and older. Since it broke code that previously compiled, the change was technically backward-incompatible, but presumably broke sufficiently few actual crates that it was deemed worth it. Surprising as it is, it's quite possible that "255".parse::<u8>() + 1 will become a compile-time error in a later release.