Parcours
/
Cairo
Cairo
/
Programme
/
Gestion des erreurs
Ge

Gestion des erreurs en Cairo

1 exercice

À propos de Gestion des erreurs

Cairo propose des techniques de gestion des erreurs pour traiter les problèmes et gérer les erreurs, ce qui permet d'exécuter le code sans encombre.

Il existe deux types d'erreurs en Cairo :

  • Les erreurs irrécupérables avec panic : elles interrompent immédiatement le programme.
  • Les erreurs récupérables avec Result : elles peuvent être traitées pendant l'exécution.

Les erreurs irrécupérables avec panic

En Cairo, des problèmes inattendus peuvent survenir pendant l'exécution d'un programme, provoquant des erreurs à l'exécution appelées « panics ». Cela arrête l'exécution du programme, ce qui est utile lorsque continuer n'aurait aucun sens. Un panic peut survenir de manière involontaire, à cause d'actions qui font paniquer le code (par exemple, accéder à un tableau au-delà de ses limites), ou délibérément, en appelant la fonction panic.

fn main() {
    let data = array![1, 2];

    if true {
        panic(data);
    }
    println!("This line will not be reached.");
}

L'exécution de cet exemple afficherait ceci dans la console :

Run panicked with [1, 2, ].

Il existe aussi une macro spéciale, appelée panic!, qui permet d'utiliser un ByteArray comme message d'erreur.

panic!("The error for the panic! Error message is not limited to 31 characters anymore");

La macro assert!

La macro assert! est un outil utile pour imposer des conditions précises dans le code. Si la condition dans assert! vaut false, le programme panique avec un message d'erreur de type ByteArray. On l'utilise souvent pour vérifier des hypothèses pendant le développement et s'assurer que les valeurs respectent certains critères.

Par exemple :

fn main() {
    let x = 5;
    assert!(x > 0, "x must be greater than zero");
}

Si x n'est pas supérieur à zéro, le programme panique avec le message "x must be greater than zero". assert! est utile pour vérifier des invariants et des préconditions sans écrire manuellement du code de gestion d'erreurs.

La notation nopanic

La notation nopanic de Cairo indique qu'une fonction ne paniquera jamais.

fn function_never_panic() -> felt252 nopanic {
    108
}

Une fonction nopanic ne peut être appelée que depuis une fonction elle aussi annotée nopanic.

L'attribut panic_with

L'attribut panic_with de Cairo sert à marquer une fonction qui renvoie un Option ou un Result ; il crée un wrapper pour la fonction annotée, qui paniquera si la fonction renvoie Option::None ou Result::Err.

#[panic_with('value is 0', wrap_not_zero)]
fn check_not_zero(value: u128) -> Option<u128> {
    if value == 0 {
        Option::None
    } else {
        Option::Some(value)
    }
}

fn main() {
    check_not_zero(0); // -> returns Option::None
    wrap_not_zero(0);  // -> panics with 'value is 0'
}

Les erreurs récupérables avec Result

L'énumération Result gère les erreurs proprement : elle fournit un retour au lieu de planter ou de produire un comportement indéfini.

enum Result<T, E> {
    Ok: T,
    Err: E,
}

Voyons un exemple d'utilisation de cette énumération pour gérer une division par zéro :

fn divide(a: u32, b: u32) -> Result<u32, ByteArray> {
    if b == 0 {
        return Result::Err("Error: Division by zero"); // Return an error if division by zero is attempted
    }
    Result::Ok(a / b)
}

fn main() {
    // Test with division by zero
    match divide(10, 0) {
        Result::Ok(result) => println!("Division successful: {}", result),
        Result::Err(e) => println!("{}", e),
    }
}

L'opérateur ?

L'opérateur ? permet une gestion des erreurs concise et implicite : il laisse la fonction appelante s'occuper des erreurs éventuelles. Quand on utilise l'opérateur ? sur un type Result ou Option, voici ce qu'il fait :

  • Si la valeur est Result::Ok(x) ou Option::Some(x), il renvoie directement la valeur interne x.
  • Si la valeur est Result::Err(e) ou Option::None, il propage l'erreur ou None en quittant immédiatement la fonction.
fn parse_u8(s: felt252) -> Result<u8, felt252> {
    match s.try_into() {
        Option::Some(value) => Result::Ok(value),
        Option::None => Result::Err('Invalid integer'),
    }
}

fn do_something_with_parse_u8(input: felt252) -> Result<u8, felt252> {
    let input_to_u8: u8 = parse_u8(input)?;
    // DO SOMETHING
    let res = input_to_u8 - 1;
    Result::Ok(res)
}

fn main() {
    let number: felt252 = 258;
    match do_something_with_parse_u8(number) {
        Result::Ok(value) => println!("Result: {}", value),
        Result::Err(e) => println!("Error: {}", e),
    }
}
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Gestion des erreurs

L'entraînement est verrouillé

Déverrouille 1 exercice de plus pour t'entraîner sur Gestion des erreurs