【小测试】rust中的无符号整数溢出

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


1.在编译阶段就可以识别出来的溢出

fn main(){
    let a : usize = 1;
    println!("{}",a-2);
}

执行rustc报以下错误:

rustc usize_test.rs 
error: this arithmetic operation will overflow
 --> usize_test.rs:4:19
  |
4 |     println!("{}",a-2);
  |                   ^^^ attempt to compute `1_usize - 2_usize`, which would overflow
  |
  = note: `#[deny(arithmetic_overflow)]` on by default

error: aborting due to previous error

2.运行期溢出

use std::time::{SystemTime, UNIX_EPOCH};

fn main() {
    let start = SystemTime::now();
    let since_the_epoch = start
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards");
    let ts = since_the_epoch.as_secs() as usize;
    //
    let a:usize = 1;
    let b :usize= ts % 10 + 2;
    println!("a={:?}, b={:?}", a,b);
    //
    println!("{:?}", a-b);
}

当溢出发生的时候,程序panic了:

thread 'main' panicked at 'attempt to subtract with overflow', usize_test_3.rs:16:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

由此也说明,rust不是真正的零成本抽象。为了防止无符号整形的溢出,必然需要在减法之前编译进去一个范围判断。

3.如何才能做溢出不panic的减法

use std::time::{SystemTime, UNIX_EPOCH};

fn main() {
    let start = SystemTime::now();
    let since_the_epoch = start
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards");
    let ts = since_the_epoch.as_secs() as usize;
    //
    let a:usize = 1;
    let b :usize= ts % 10 + 2;
    println!("a={:?}, b={:?}", a,b);
    //
    println!("{:?}", a.wrapping_sub(b));
}

使用wrapping_sub()这个方法。

rust是如此的严谨,情愿使用的时候很啰嗦,也不要在运行时挖坑。

4. 通过release版本跳过溢出检查

在新的目录,执行:cargo new usize_test
在上面的代码写在:usize_test/src/main.rs 中
在usize_test目录执行:cargo build --release
然后执行 usize_test/target/release/usize_test,发现usize类型的溢出已经不会panic了,其结果与执行 wrapping_sub() 一样。
(感谢zhihu网友 @QDelta​ 提醒。)

不过我又陷入了迷惑:

  1. 既然release版本就不检查usize溢出了,还要wrapping_sub()这样的方法干啥?
  2. 对于溢出这样的情况,应该有程序员通过不同的写法来决定如何处理。如果编译个release版本就能跳过,rust又和C/C++有啥区别?

posted on 2022-04-15 15:52  ahfuzhang  阅读(239)  评论(0编辑  收藏  举报