为什么 Rust 在 --release 中执行整数溢出检查?

Ale*_*vik 8 integer-overflow rust rust-cargo

我有这段简单的代码:

let val: u8 = 255 + 1;
println!("{}", val);
Run Code Online (Sandbox Code Playgroud)

这里据说这样的代码如果使用--releaseflag运行的话会正常编译。

我通过 运行此代码cargo run --release,并且仍然看到检查:

error: this arithmetic operation will overflow
 --> src/main.rs:2:19
  |
2 |     let val: u8 = 255 + 1;
  |                   ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
  |
  = note: `#[deny(arithmetic_overflow)]` on by default

error: could not compile `rust-bin` due to previous error
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?

use*_*342 15

书上说得有点不严谨。调试和发布模式下都不允许溢出,只是发布模式出于性能原因省略了运行时检查(用溢出代替它们,CPU 通常都会这样做)。静态检查不会被删除,因为它们不会影响生成代码的性能。这在发布模式下打印 0 并在调试1下出现恐慌:

let x: u8 = "255".parse().unwrap();
let val: u8 = x + 1;
println!("{}", val);
Run Code Online (Sandbox Code Playgroud)

您可以使用 禁用编译时检查#[allow(arithmetic_overflow)]。这也会在发布模式下打印 0 并在调试模式下出现恐慌:

#[allow(arithmetic_overflow)]
let val: u8 = 255 + 1;
println!("{}", val);
Run Code Online (Sandbox Code Playgroud)

正确的做法是不依赖于发布模式的这种行为,而是告诉编译器你想要什么。在调试和发布模式下都会打印 0:

let val: u8 = 255u8.wrapping_add(1);
println!("{}", val);
Run Code Online (Sandbox Code Playgroud)

1 该示例使用的"255".parse()原因是,令我惊讶的是,它let x = 255u8; let val = x + 1; 无法编译- 换句话说,rustc 不仅可以防止常量算术中的溢出,而且还可以在其他任何可以证明溢出发生的地方。该更改显然是在 Rust 1.45 中进行的,因为它是在Rust 1.44 及更早版本中编译的。由于它破坏了之前编译的代码,因此该更改在技术上是向后不兼容的,但大概破坏了足够少的实际板条箱,因此被认为是值得的。令人惊讶的是,这很可能会"255".parse::<u8>() + 1在以后的版本中成为编译时错误。