Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Rust, Why does integer overflow sometimes cause compilation error or runtime error?

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=1 environment 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?

like image 441
Junhee Avatar asked Nov 29 '25 17:11

Junhee


1 Answers

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:

  • without 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
        }
    [...]
    
  • with 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

like image 81
cafce25 Avatar answered Dec 02 '25 12:12

cafce25



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!