当通过列表向后循环时,惊慌失措'尝试减去溢出'

lma*_*ens 18 integer-overflow rust integer-arithmetic

我正在编写一个循环方法,用于向索引向前或向后移动索引.以下代码用于向后循环:

(i-1)%list_length
Run Code Online (Sandbox Code Playgroud)

在这种情况下,i属于类型usize,这意味着它是无符号的.如果i等于0,则会导致"尝试减去溢出"错误.我尝试使用正确的转换方法来解决此问题:

((i as isize)-1)%(list_length as isize)) as usize
Run Code Online (Sandbox Code Playgroud)

这导致整数溢出.

我理解错误发生的原因,目前我通过检查索引是否等于0来解决问题,但我想知道是否有某种方法可以通过将变量转换为正确的类型来解决它.

She*_*ter 11

作为DK.指出,你不希望在整数级别包装语义:

fn main() {
    let idx: usize = 0;
    let len = 10;

    let next_idx = idx.wrapping_sub(1) % len;
    println!("{}", next_idx) // Prints 5!!!
}
Run Code Online (Sandbox Code Playgroud)

相反,您希望使用模数逻辑来回绕:

let next_idx = (idx + len - 1) % len;
Run Code Online (Sandbox Code Playgroud)

这仅在len+ idx小于类型的最大值时才有效- 这更容易用a u8代替usize; 只需设置idx为200和len250.

如果你不能保证这两个值之和总是小于最大值,我可能会使用"已检查"系列操作.这与您已经提到的相同级别的条件检查有关,但整齐地绑在一行中:

let next_idx = idx.checked_sub(1).unwrap_or(len - 1);
Run Code Online (Sandbox Code Playgroud)


lje*_*drz 6

如果您的代码可能有溢出操作,我建议使用Wrapping.允许时,您无需担心抛出或溢出恐慌:

use std::num::Wrapping;

let zero = Wrapping(0u32);
let one = Wrapping(1u32);

assert_eq!(std::u32::MAX, (zero - one).0);
Run Code Online (Sandbox Code Playgroud)

  • 对于包装算术,还有[每种类型的固有方法](https://doc.rust-lang.org/std/primitive.usize.html#method.wrapping_sub). (4认同)