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


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

(i-1)%list_length

在这种情况下, i 属于那种类型 usize,这意味着它是未签名的。如果 i 等于0,这导致'尝试减去溢出'错误。我尝试使用正确的转换方法来解决此问题:

((i as isize)-1)%(list_length as isize)) as usize

这导致整数溢出。

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


2111
2017-12-23 12:24


起源

暂且不说:我认为你不想这样做 一点都不  (-1 % 10) 是 -1不是 9。 -1isize as usize 是 18446744073709551615 (在64位架构上)。 - DK.
好的,我不知道。我认为它的工作方式与之相似 这个帖子,但我现在看到它的实现方式如上所述 这个帖子。这清除了! - blackplant


答案:


DK。指出,您不希望在整数级别包装语义:

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

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

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

let next_idx = (idx + len - 1) % len;

这只适用于 len + idx 小于该类型的最大值 - 这更容易看到一个 u8 代替 usize;刚设置 idx 到200和 len 到250。

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

let next_idx = idx.checked_sub(1).unwrap_or(len - 1);

5
2017-12-23 14:17





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

use std::num::Wrapping;

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

assert_eq!(std::u32::MAX, (zero - one).0);

4
2017-12-23 12:31



还有 每种类型的固有方法 用于包装算术。 - Shepmaster