Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/compiler/pitfalls/arithmetic-overflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,35 @@ let v = x as u8;
println!("{}", v)
```

最终程序会输出`255`, 因此大家可能会下意识地就觉得算数操作在 Rust 中只会导致结果的不正确,并不会导致异常。但是实际上,如果是因为算术操作符导致的溢出,就会让整个程序 panic:
最终程序会输出`255`, 因此大家可能会猜测算术溢出在 Rust 中也会只导致截断。但是实际上,如果在 debug 模式下运行,算术溢出会引发 panic:

```rust
#[allow(arithmetic_overflow)] // 阻止编译器在编译时发现问题
fn main() {
let x: u8 = 10;

let v = x + u8::MAX;
println!("{}", v)
}
```

输出结果如下:
而在 release 模式下为了最大化性能,编译器不会插入算术溢出检查,所以不会导致 panic,而是变成[二补数](https://stackoverflow.com/a/60238510/19171888)。

```console
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:5:13
```
如何理解这种拧巴的行为?为了处理算术溢出,Rust 的基本运算实则以下关联函数:

那么当我们确实有这种需求时,该如何做呢?可以使用 Rust 提供的`checked_xxx`系列方法:
- `wrapping_xxx`:正常时返回数值,溢出时回到值域的另一端(`MAX+1=MIN`, `MIN-1=MAX`)
- `check_xxx`:正常时返回`Some(x)`,溢出时返回`None`
- `unchecked_xxx`:正常时返回数值,溢出时 UB,不安全
- `saturating_xxx`:正常时返回数值,溢出时定在值域边缘(`MAX+1=MAX`, `MIN-1=MIN`)
- `overflowing_xxx`:正常时返回`(x, false)`,溢出时返回`(wrapped(x), true)`
- `strict_xxx`:正常时返回数值,溢出时 panic

```rust
fn main() {
let x: u8 = 10;
特别地,除以`0`在任何情况下都会被检查。

let v = x.checked_add(u8::MAX).unwrap_or(0);
println!("{}", v)
}
运算符的默认行为在 debug 模式下是`strict_xxx`,release 模式下是`wrapping`。想在 release 模式下也默认到`strict_xxx`,可以在`Cargo.toml`加上:

```toml
[profile.release]
overflow-checks = true
```

也许你会觉得本章内容其实算不上什么陷阱,但是在实际项目快速迭代中,越是不起眼的地方越是容易出错:
Expand All @@ -59,11 +62,12 @@ pub fn working_items_per_minute(speed: u8) -> u32 {
}
```

上述代码中,`speed * cph`就会直接 panic:
上述代码在 debug 模式下`speed * cph`就会直接 panic:

```console
thread 'main' panicked at 'attempt to multiply with overflow', src/main.rs:10:18
```

是不是还藏的挺隐蔽的?因此大家在 Rust 中做数学运算时,要多留一个心眼,免得上了生产才发现问题所在。或者,你也可以做好单元测试:)
release 模式下则输出`72.9`。

是不是还藏的挺隐蔽的?因此大家在 Rust 中做数学运算时,要多留一个心眼,免得上了生产才发现问题。或者直接打开`overflow-checks`直接 panic。