[Rust] 对整形溢出的处理

发布时间 2023-11-05 23:42:26作者: 是兔子不是秃子

1. 两种不同模式下的整形溢出

坑了个爹的,书上说的没理解清楚,在Rust程序语言设计中文版3.2中提到了,当使用--release参数进行发布模式构建时,Rust不会检测导致panic的整形溢出,这里需要分两种情况考虑:

  • 编译期就可以发现的整形溢出
  • 程序运行过程中会发生的整形溢出

1.1 编译阶段

如果是编译期能够确定会发生的整形溢出(程序1-1),无论是不是执行了--release参数都会在构建阶段(cargo build)发生报错

// 程序1-1
fn main() {

    let i:u8 = 254;

    let i = i + 4;

    println!("{}!",i);
}

上述变量i的类型为u8类型,值的范围在(0..=255)

经过运算的结果最终的i的值为258,这个值在编译期已经可以检测到整形溢出,最终构建会报错如下:

  • debug模式

    image-20231105214716809

  • release模式

    image-20231105214942664

    这种编译期可以确定的溢出错误,两种模式下都会发生崩溃

1.2 运行阶段

在运行期发生的整形溢出(程序1-2),不会在build阶段报错

// 程序1-2
fn main() {
    let i:u8 = 254;
    for j in 0..=4 {
        println!("{}!",i+j);
    }
}

上述代码,只有在j>1的情况下才会发生整形溢出,这种错误在编译期是发现不了的

  • debug模式执行

    image-20231105220421439

    在不使用--release的时候,在运行阶段报错

  • release模式

    image-20231105220740215

    在使用--release 的情况下,溢出值对256进行了取模运算

1.3 说明

当使用--release进行构建发布模式时,当检测到整形溢出,将会使用一种进制补码包裹(two’s complement wrapping的操作。就是最终的计算出来的结果对2N取模。N为类型的bit位,u8是8位,N=8

2 Rust对溢出的操作

摘自:数据类型 - Rust 程序设计语言 中文版 (rustwiki.org)

要显式处理溢出的可能性,可以使用标准库针对原始数字类型提供的以下一系列方法:

  • 使用 wrapping_* 方法在所有模式下进行包裹,例如 wrapping_add
  • 如果使用 checked_* 方法时发生溢出,则返回 None
  • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
  • 使用 saturating_* 方法使值达到最小值或最大值

这么处理,在debug模式下就不会panic

2.1 wrapping_*

当计算结果发生溢出执行取模操作(程序2-1)

// 2-1
fn main() {
    let i:u8 = 254;
    for j in 2..=4 {
        println!("=======================");
        println!("i+j={}!",i.wrapping_add(j));
        println!("j-i={}!",j.wrapping_sub(i));
        println!("j*i={}!",j.wrapping_mul(i));
        println!("j/i={}!",j.wrapping_div(i));
    }
}

结果如下:

image-20231105222530325

那么问题来了,负数怎么取整??

你真的搞懂了负数取模吗?-CSDN博客

2.2 checked_*

当发生溢出时返回None,否则返回Some(num) => option<u8>(程序2-2)

// 2-2
fn Print(a:Option<u8>) -> String
{
    match a {
        Some(num) => num.to_string(),
        None => "None".to_string(),
    }
}

fn main() {
    let i:u8 = 254;
    for j in 0..=4 {
        println!("====i={}, j={}",i,j);
        println!("i+j={}!",Print(i.checked_add(j)));
        println!("j-i={}!",Print(j.checked_sub(i)));
        println!("j*i={}!",Print(j.checked_mul(i)));
        println!("j/i={}!",Print(j.checked_div(i)));
    }
}

结果如下:

Option输出方式:Rust 学习之 Some、Option、Result | VGOT Blog

image-20231105225954235

我太菜了,报警告了,问题不大

2.3 overflowing_*

返回计算结果数值和是否溢出的布尔值(程序2-3)

// 2-3
fn main() {
    let i:u8 = 254;
    for j in 0..=4 {
        println!("====i={}, j={}",i,j);
        println!("i+j={:?}!",i.overflowing_add(j));
        println!("j-i={:?}!",j.overflowing_sub(i));
        println!("j*i={:?}!",j.overflowing_mul(i));
        println!("j/i={:?}!",j.overflowing_div(i));
    }
}

运行结果如下:

数组输出方式: Rust 元组(Tuple)入门指南 - 无涯教程网 (learnfk.com)

image-20231105231019033

bool值反映的是有没有发生溢出

2.4 saturating_*

发生溢出之后取自身类型的极值(程序2-4)

// 2-4
fn main() {
    let i:u8 = 254;
    for j in 0..=4 {
        println!("====i={}, j={}",i,j);
        println!("i+j={:?}!",i.saturating_add(j));
        println!("j-i={:?}!",j.saturating_sub(i));
        println!("j*i={:?}!",j.saturating_mul(i));
        println!("j/i={:?}!",j.saturating_div(i));
    }
}

结果如下

image-20231105231322330