Rust Overflow in Debug and Release
Overflow checks are checks that stop a program when a number result does not fit its type. The same sum can panic in one build and wrap in the other.
When the compiler can see it coming
Written with plain numbers the compiler can check, an overflowing sum never runs at all. This code is refused while compiling, with the error "this arithmetic operation will overflow":
let m: u8 = 255;
let total = m + 1;
println!("total = {}", total);When the number arrives while running
Hide the value from the compiler, and it can no longer see the overflow coming. black_box keeps the number a secret until the program runs. Build this program the usual way and it panics with "attempt to add with overflow". Build it with --release and it calmly prints total = 0:
fn main() {
let m = std::hint::black_box(255u8);
let total = m + 1;
println!("total = {}", total);
}Ask the program which build it is
cfg! asks the compiler a yes-or-no question while compiling. In a debug build this prints true. In a release build the same program prints false:
let checks = cfg!(debug_assertions);
println!("overflow checks on = {}", checks);Turn the checks on for release builds
Two lines in your project's Cargo.toml keep the checks in release builds. Release builds then panic on overflow, just like debug builds:
[profile.release]
overflow-checks = trueExample
fn main() {
let checks_on = cfg!(debug_assertions);
println!("overflow checks on = {}", checks_on);
let m: u8 = 255;
println!("checked = {:?}", m.checked_add(1));
println!("wrapped = {}", m.wrapping_add(1));
}Key points
- A debug build is what cargo run and cargo build make.
- Overflow checks are on.
- An overflowing sum panics with the message "attempt to add with overflow".
- A release build is what cargo build --release makes.
- The checks are off for speed.
- The same sum wraps silently.
- When the compiler can see the overflow coming, it refuses while compiling — no build of either kind ever runs
- Use cfg!(debug_assertions) to ask, inside the program, which kind of build it is.
- It is true in debug builds.
- It is false in release builds.
- Set overflow-checks = true in the [profile.release] part of Cargo.toml when a release build must keep the checks
In this example, the program first asks which build it is. The output above is the debug run: checks are on. checked_add and wrapping_add answer the same in every build — None, and 0 — because the method families never change. Only plain + changes between builds: it would panic in this build, and it would wrap to 0 in a release build. One program, two honest reports.
Note
The four method families — checked_, wrapping_, saturating_, overflowing_ — mean the same thing in every build.
Only plain operators change their behaviour.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
You ship a release build. A u8 sum inside it passes 255 while the program runs. What happens?