在Rust中,为什么整数溢出有时会导致编译错误或运行时错误?

Jun*_*hee 20 integer-overflow rust

fn main() {
    let num: u8 = 255;
    let num2: u8 = num + 1;
    println!("{}, {}", num, num2);
}

Run Code Online (Sandbox Code Playgroud)

当 时$ cargo build --release,这段代码不会产生编译错误。并且$ cargo run,产生运行时错误。

线程“main”因“尝试添加溢出”而惊慌失措,src/main.rs:3:20 注意:使用RUST_BACKTRACE=1环境变量运行以显示回溯

这没关系。但我不明白的是下面的情况。当我删除 println 行时,会出现编译错误。

fn main() {
    let num: u8 = 255;
    let num2: u8 = num + 1;
}
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

为什么整数溢出有时会导致编译错误或运行时错误?

caf*_*e25 9

当编译器可以简单地证明它会在运行时溢出时,这将是一个编译时错误,当您删除时会发生这种情况,println因为 thennum可以轻松内联(无论如何它只在那个地方使用),但println很难优化因为它需要引用它的参数,并且不容易证明不同的地址对它没有影响(也考虑到有一个fmt::Pointer)所有这些导致了这样一个事实:证明第一种情况并不那么简单,其中num不能那么容易内联。

作为参考,这里是每个变体中第一个和第二个变量的 mir 表示,您可以看到其中一个变量已经被替换为u8::MAX

  • 没有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
        }
    [...]
    
    Run Code Online (Sandbox Code Playgroud)
  • 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
        }
    [...]
    
    Run Code Online (Sandbox Code Playgroud)

其中,在这两种情况下_1_3_4分别对应于、行中被分配num的值以及和的检查加法的结果。numnum2num1

经过更多的实验后,println罪魁祸首并不是,而只是参考num

  • 内联是关于函数的。您可以在编译时计算“num2”,而无需抑制“num”的定义。当然,目前的启发式可能不会,但从理论的角度来看,确实没有什么可以阻止计算。如果 `println!` 位于定义之间,那么这将非常不同,但当前情况(无论是否为 println)都是微不足道的。 (2认同)