Rust中整数溢出为何有时触发编译错误有时触发运行时错误?
场景示例
代码示例1
fn main() { let num: u8 = 255; let num2: u8 = num + 1; println!("{}, {}", num, num2); }
执行$ cargo build --release时无编译错误,但运行$ cargo run会触发运行时panic:
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:20
note: run withRUST_BACKTRACE=1environment variable to display a backtrace
代码示例2
fn main() { let num: u8 = 255; let num2: u8 = num + 1; }
执行$ cargo build --release直接触发编译错误:
$ 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
核心原因
这两种差异是Rust编译期静态检查和运行时动态检查的触发逻辑不同导致的:
编译期检查:只拦截100%确定的溢出
Rust编译器会在编译阶段做静态分析,只有当它能绝对判定某段整数运算必然会溢出时,才会抛出编译错误(默认开启#[deny(arithmetic_overflow)]规则)。
示例2中,num是固定的u8最大值255,加1的溢出是编译时就能直接推导的必然结果,所以编译器直接报错拦截。运行时检查:处理静态分析无法确定的情况
如果编译器没法通过静态分析判定运算是否一定会溢出,就会把检查逻辑留在程序运行阶段。
示例1中,因为num2被println语句使用了,编译器会认为这个变量有实际用途,不会把这段运算判定为“无意义的必然溢出代码”,因此编译阶段不会报错。等到程序运行时,溢出实际发生,Rust就会触发panic终止程序。
额外补充:在release模式下,编译器会对无意义的代码做更激进的优化,如果溢出运算的结果完全没被使用(比如示例2),编译器能明确判定这是无效且必然溢出的代码,就直接在编译期报错;但如果结果被使用,编译器无法直接抹除这段逻辑,就只能留到运行时检查。
内容的提问来源于stack exchange,提问作者Junhee

