fn main() {
let num: u8 = 255;
let num2: u8 = num + 1;
println!("{}, {}", num, num2);
}
When $ cargo build --release, this code doesn't make compile error.
And $ cargo run, make runtime error.
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:20 note: run with
RUST_BACKTRACE=1environment variable to display a backtrace
This is okay. But what I don't understand is the situation below. When I delete println line, it makes compile error.
fn main() {
let num: u8 = 255;
let num2: u8 = num + 1;
}
$ cargo build --release
error: this arithmetic operation will overflow
--> src/main.rs:3:20
|
3 | let num2: u8 = num + 1;
| ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
|
= note: `#[deny(arithmetic_overflow)]` on by default
Why does integer overflow sometimes cause compilation errors or runtime error?
It's going to be a compile time error when the compiler can trivially prove it's going to overflow at runtime, that happens when you remove the println because then num can be inlined easily (it's only used in that one spot anyways), but println is very hard to optimize around because it takes references to it's arguments and it's not easily provable that different addresses don't make a difference for it (consider also there is a fmt::Pointer) all of this leads to the fact that it's not as trivial to prove for the first case where num can't be inlined that easily.
For reference here are the mir representations of the first and second variable in each variant where you can see that in one the variable is replaced with u8::MAX already:
println:
[...]
bb0: {
_1 = const u8::MAX; // scope 0 at plain_overflow.rs:2:19: 2:22
_3 = const u8::MAX; // scope 1 at plain_overflow.rs:3:20: 3:23
_4 = CheckedAdd(_3, const 1_u8); // scope 1 at plain_overflow.rs:3:20: 3:27
assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", move _3, const 1_u8) -> bb1; // scope 1 at main.rs:3:20: 3:27
}
[...]
println:
[...]
bb0: {
_1 = const u8::MAX; // scope 0 at with_print.rs:2:19: 2:22
_3 = _1; // scope 1 at with_print.rs:3:20: 3:23
_4 = CheckedAdd(_3, const 1_u8); // scope 1 at with_print.rs:3:20: 3:27
assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", move _3, const 1_u8) -> bb1; // scope 1 at with_print.rs:3:20: 3:27
}
[...]
where in both cases _1, _3 and _4 correspond to num, the value of num in the line where num2 is assigned and the result of a checked additon of num and 1 respectively.
After some more experimentation, not println is the culprit but merely taking a reference to num
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With