Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Traitements des entiers

Dépassement d'entiers

Bien que des vérifications soient effectuées par Rust en ce qui concerne les potentiels dépassements d'entiers, des précautions doivent être prises lors de l'exécution d'opérations arithmétiques sur les entiers.

En particulier, il faut noter que le profil de compilation (généralement dev, la compilation de débogage par défaut, ou release, la compilation optimisée standard) modifie le comportement en cas de dépassement d'entier. En configuration dev, un dépassement provoque l'arrêt du programme (panic), tandis qu'en configuration release, la valeur calculée est silencieusement tronquée au nombre de bits du type numérique, ce qui donne une sémantique d'arithmétique circulaire (wrap-around).

Le comportement en cas de dépassement d'entier peut être défini explicitement par l'option de compilation -C overflow-checks=true (ou false). Il peut également être modifié dans la définition du profil dans Cargo.toml :

[profile.release]
overflow-checks = true  # enable overflow checks in release builds

Lorsqu'un dépassement est possible, le comportement peut être rendu explicite soit en utilisant des méthodes spécifiques, soit en utilisant des types enveloppants spécifiques.

Les méthodes sont de la forme <mode>_<op>, où <mode> est checked, overflowing, wrapping ou saturating, et <op> est add, mul, sub, shr, etc. Les sémantiques sont les suivantes :

  • checked_<op> retourne None en cas de dépassement,
  • overflowing_<op> retourne à la fois un résultat selon l'arithmétique circulaire et un booléen indiquant si un dépassement a eu lieu,
  • wrapping_<op> retourne toujours le résultat selon l'arithmétique circulaire,
  • saturating_<op> retourne toujours le résultat saturé.

Les types enveloppants sont Wrapping<T> et Saturating<T> (de std::num), où T est un type entier. Le premier fournit une sémantique d'arithmétique circulaire pour toutes les opérations arithmétiques, tandis que le second fournit une sémantique de saturation. Une fois les valeurs enveloppées, toutes les opérations suivantes sont effectuées avec la sémantique correspondante.

#![allow(unused)]
#![allow(dead_code)]
fn main() {
use std::num::Wrapping;
use std::panic;

fn use_integer() {
    let x: u8 = 242;

    let result = panic::catch_unwind(|| {
        println!("{}", x + 50); // panics in debug, prints 36 in release.
    });
    if result.is_err() {
        println!("panic");
    }
    // always prints 36.
    println!("{}", x.overflowing_add(50).0);
    // always prints 36.
    println!("{}", x.wrapping_add(50));
    // always prints 36.
    println!("{}", Wrapping(x) + Wrapping(50));

    // always panics:
    let (res, c) = x.overflowing_add(50);
    let result = panic::catch_unwind(|| {
        if c {
            panic!("custom error");
        } else {
            println!("{}", res);
        }
    });
    if result.is_err() {
        println!("panic");
    }
}
}

Lorsqu'une opération arithmétique peut produire un dépassement, les opérateurs classiques sur les entiers NE DOIVENT PAS être utilisés. Les méthodes spécialisées comme checked_<op>, overflowing_<op>, wrapping_<op>, ou saturating_<op>, ou des types enveloppants spécialisés comme Wrapping ou Saturating, DOIVENT être utilisés pour rendre le comportement explicite et homogène, quel que soit le profil de compilation.