web-dev-qa-db-fra.com

Paniqué à «tenter de soustraire avec débordement» lors d'un retour en arrière dans une liste

J'écris une méthode de cycle pour une liste qui déplace un index vers l'avant ou vers l'arrière. Le code suivant est utilisé pour reculer:

(i-1)%list_length

Dans ce cas, i est du type usize, ce qui signifie qu'il n'est pas signé. Si i est égal à 0, cela conduit à une erreur "tentative de soustraction avec débordement". J'ai essayé d'utiliser les méthodes de casting correctes pour contourner ce problème:

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

Il en résulte un débordement d'entier.

Je comprends pourquoi les erreurs se produisent et, pour le moment, j'ai résolu le problème en vérifiant si l'index est égal à 0, mais je me demandais s'il y avait un moyen de le résoudre en convertissant les variables en types corrects.

17
lmartens

Comme DK. Souligne , vous ne voulez pas encapsuler la sémantique au niveau entier:

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

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

Au lieu de cela, vous souhaitez utiliser la logique modulo pour boucler:

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

Cela ne fonctionne que si len + idx est inférieur au maximum du type - c'est beaucoup plus facile à voir avec un u8 au lieu de usize; définissez simplement idx sur 200 et len sur 250.

Si vous ne pouvez pas garantir que la somme des deux valeurs sera toujours inférieure à la valeur maximale, j'utiliserais probablement la famille d'opérations "vérifiées". Cela fait le même niveau de vérification conditionnelle que vous avez mentionné, mais est soigneusement lié à une seule ligne:

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

Si votre code peut avoir des opérations débordantes, je vous suggère d'utiliser Wrapping . Vous n'avez pas à vous soucier du casting ou des débordements de panique lorsque vous l'autorisez:

use std::num::Wrapping;

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

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