web-dev-qa-db-ja.com

リストを逆方向に循環するときに「オーバーフローで減算しようとする」でパニック

インデックスを前方または後方に移動するリストのサイクルメソッドを記述しています。次のコードを使用して逆方向に循環します。

(i-1)%list_length

この場合、iusize型であり、符号なしであることを意味します。 iが0に等しい場合、「オーバーフローを伴う減算」エラーが発生します。この問題を回避するために正しいキャスト方法を使用しようとしました。

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

これにより、整数オーバーフローが発生します。

私はエラーが発生する理由を理解しています。現時点では、インデックスが0に等しいかどうかを確認することで問題を解決しましたが、変数を正しい型にキャストすることで解決する方法があるかどうか疑問に思っていました。

17
lmartens

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に設定するだけです。

2つの値の合計が常に最大値よりも小さくなることを保証できない場合は、おそらく「チェック済み」の操作ファミリを使用します。これは、既に述べたのと同じレベルの条件チェックを行いますが、きちんと1行に結び付けられています。

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

コードでオーバーフロー操作が発生する可能性がある場合は、 Wrapping を使用することをお勧めします。許可すると、キャストやオーバーフローのパニックを心配する必要はありません。

use std::num::Wrapping;

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

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