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 :
panic : elles interrompent immédiatement le programme.Result : elles peuvent être traitées pendant l'exécution.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");
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.
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.
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'
}
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 ? 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 :
Result::Ok(x) ou Option::Some(x), il renvoie directement la valeur interne x.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),
}
}