Tracce
/
Cairo
Cairo
/
Programma
/
Gestione degli errori
Ge

Gestione degli errori in Cairo

1 esercizio

Informazioni su Gestione degli errori

Cairo offre tecniche di gestione degli errori per affrontare i problemi e gestirli, permettendo di eseguire il codice senza intoppi durante l'esecuzione.

In Cairo esistono due tipi di errori:

  • Errori non recuperabili con panic: questi errori terminano il programma immediatamente.
  • Errori recuperabili con Result: questi errori possono essere gestiti durante l'esecuzione.

Errori non recuperabili con panic

In Cairo, durante l'esecuzione del programma possono verificarsi problemi imprevisti, che danno origine a errori a runtime chiamati «panic». Questo ferma l'esecuzione del programma, cosa utile quando continuare non avrebbe alcun senso. Un panic può verificarsi in modo involontario, tramite azioni che fanno andare in panic il codice (ad esempio accedere a un array oltre i suoi limiti), oppure in modo intenzionale, chiamando la funzione panic.

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

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

Eseguendo questo esempio, nella console verrebbe stampato quanto segue:

Run panicked with [1, 2, ].

Esiste anche una macro speciale, chiamata panic!, che permette di usare un ByteArray come messaggio di errore.

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

La macro assert!

La macro assert! è uno strumento utile per imporre condizioni specifiche nel codice. Se la condizione in assert! restituisce false, il programma andrà in panic con un messaggio di errore di tipo ByteArray. Viene spesso usata per verificare le assunzioni durante lo sviluppo e per assicurarsi che i valori rispettino determinati criteri.

Ad esempio:

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

Se x non è maggiore di zero, il programma andrà in panic con il messaggio "x must be greater than zero". assert! è utile per controllare invarianti e precondizioni senza scrivere manualmente codice di gestione degli errori.

La notazione nopanic

La notazione nopanic di Cairo indica che una funzione non andrà mai in panic.

fn function_never_panic() -> felt252 nopanic {
    108
}

Una funzione nopanic può essere chiamata solo all'interno di una funzione anch'essa annotata come nopanic.

L'attributo panic_with

L'attributo panic_with di Cairo serve a contrassegnare una funzione che restituisce un Option o un Result, e crea un wrapper per la funzione annotata che andrà in panic se la funzione restituisce Option::None o 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'
}

Errori recuperabili con Result

L'enum Result gestisce gli errori in modo elegante, fornendo un feedback invece di andare in crash o produrre comportamenti indefiniti.

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

Vediamo un esempio di come potremmo usare questo enum per gestire una divisione per zero:

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'operatore ?

L'operatore ? si usa per una gestione degli errori concisa e implicita, lasciando che sia la funzione chiamante a occuparsi di eventuali errori. Quando usi l'operatore ? su un tipo Result o Option, succede quanto segue:

  • Se il valore è Result::Ok(x) o Option::Some(x), restituisce direttamente il valore interno x.
  • Se il valore è Result::Err(e) o Option::None, propaga l'errore o None uscendo immediatamente dalla funzione.
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),
    }
}
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Gestione degli errori

La pratica è bloccata

Sblocca un altro esercizio per esercitarti su Gestione degli errori