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


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

$ 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;
}
$ 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

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

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

作为参考,这里是每个变量中第一个和第二个变量的多个表示形式,您可以看到,在其中一个变量已经被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
    }
    [...]
    
  • withprintln:
    [...]
    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
    }
    [...]
    

其中_1_3_4对应num,num2所在行的num的值以及num1的检查加法的结果。

经过更多的实验,不是println,而是仅仅引用了num

编译器警告是由linting产生的,可能很难在所有情况下静态检查算术溢出。在这种情况下,println!()在某种程度上使编译器难以检测溢出。我认为这可能值得报告一个bug?

同样,代码产生运行时错误的原因是您没有在release模式下运行它。尝试运行与cargo run --release相同的代码,然后您将得到输出

255, 0

这是因为,在release模式下,Rust不包括对导致恐慌的算术溢出的检查。相反,如果发生溢出,Rust执行two的补码包装。

相关内容

最新更新