saverioriotto.it

Error Handling in Rust: Panics, Option e Result

Scopri come gestire gli errori in Rust! Impara a utilizzare panic!, Option e Result in modo sicuro e crea errori personalizzati per un codice robusto e affidabile.

Error Handling in Rust: Panics, Option e Result

La gestione degli errori è fondamentale per scrivere codice robusto e resiliente. Rust adotta un approccio unico alla gestione degli errori, bilanciando la sicurezza con la semplicità. In questo articolo, esploreremo:

  • Come evitare i panic! e gestire errori critici.
  • L'uso di Option e Result per gestire errori comuni.
  • Come creare errori personalizzati per adattarli alle esigenze della tua applicazione.
 

Error Handling in Rust: Panics, Option e Result

Meta Description: Scopri come gestire gli errori in Rust! Impara a utilizzare panic!, Option e Result in modo sicuro e crea errori personalizzati per un codice robusto e affidabile.


La gestione degli errori è fondamentale per scrivere codice robusto e resiliente. Rust adotta un approccio unico alla gestione degli errori, bilanciando la sicurezza con la semplicità. In questo articolo, esploreremo:

  • Come evitare i panic! e gestire errori critici.
  • L'uso di Option e Result per gestire errori comuni.
  • Come creare errori personalizzati per adattarli alle esigenze della tua applicazione.

Evitare i panic! e Gestire Errori Critici

Il macro panic! viene usato quando il programma incontra un errore irrimediabile e deve terminare immediatamente. Tuttavia, i panic sono da usare con parsimonia, preferendo meccanismi più sicuri per gestire gli errori.

Esempio di panic!

fn main() {
    let numeri = vec![1, 2, 3];
    println!("{}", numeri[10]); // Questo causa un panic!
}

Quando possibile, utilizza metodi sicuri come get per prevenire panic:

fn main() {
    let numeri = vec![1, 2, 3];
    match numeri.get(10) {
        Some(valore) => println!("{}", valore),
        None => println!("Indice fuori dai limiti!"),
    }
}

Gestione degli Errori con Option e Result

Rust introduce i tipi Option e Result per rappresentare rispettivamente valori opzionali e operazioni che possono fallire.

Option: Valori Opzionali

Il tipo Option rappresenta la presenza o l'assenza di un valore.

fn trova_elemento(vettore: Vec, indice: usize) -> Option {
    vettore.get(indice).copied()
}

fn main() {
    let numeri = vec![1, 2, 3];
    match trova_elemento(numeri, 2) {
        Some(valore) => println!("Valore trovato: {}", valore),
        None => println!("Nessun valore trovato."),
    }
}

Result: Successo o Errore

Il tipo Result è usato per rappresentare operazioni che possono avere successo (Ok) o fallire (Err).

fn dividi(a: i32, b: i32) -> Result {
    if b == 0 {
        Err(String::from("Errore: Divisione per zero"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match dividi(10, 2) {
        Ok(risultato) => println!("Risultato: {}", risultato),
        Err(errore) => println!("{}", errore),
    }
}

Concatenare Operazioni con ?

L'operatore ? consente di propagare errori in modo conciso.

fn leggi_file(file: &str) -> Result {
    std::fs::read_to_string(file)
}

fn main() -> Result<(), std::io::Error> {
    let contenuto = leggi_file("testo.txt")?;
    println!("{}", contenuto);
    Ok(())
}

Creare Errori Personalizzati

Rust consente di definire errori personalizzati per rappresentare situazioni specifiche della tua applicazione.

Definire un Enum per gli Errori

Puoi creare un enum per rappresentare diversi tipi di errori.

use std::fmt;

#[derive(Debug)]
enum MioErrore {
    DivisionePerZero,
    FileNonTrovato,
}

impl fmt::Display for MioErrore {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MioErrore::DivisionePerZero => write!(f, "Errore: Divisione per zero"),
            MioErrore::FileNonTrovato => write!(f, "Errore: File non trovato"),
        }
    }
}

fn dividi(a: i32, b: i32) -> Result {
    if b == 0 {
        Err(MioErrore::DivisionePerZero)
    } else {
        Ok(a / b)
    }
}

fn main() {
    match dividi(10, 0) {
        Ok(risultato) => println!("Risultato: {}", risultato),
        Err(errore) => println!("{}", errore),
    }
}

Esercizi Pratici

  1. Implementa una Funzione Sicura di Accesso a un Vettore
    Scrivi una funzione che restituisce un valore opzionale usando Option.

    Soluzione:

    fn accesso_sicuro(vettore: Vec, indice: usize) -> Option {
        vettore.get(indice).copied()
    }
  2. Valida un Input Utente con Result
    Implementa una funzione che accetta una stringa e restituisce un numero intero o un errore.

    Soluzione:

    fn valida_input(input: &str) -> Result {
        input.parse::().map_err(|_| String::from("Input non valido"))
    }
  3. Creazione di un Sistema di Errori Personalizzato
    Crea un enum per gestire errori di rete come timeout e connessione fallita.

    Soluzione:

    #[derive(Debug)]
    enum ErroreRete {
        Timeout,
        ConnessioneFallita,
    }

Conclusione

Rust fornisce strumenti potenti per la gestione degli errori, rendendo il codice più sicuro e prevedibile. Con Option e Result, puoi evitare condizioni di errore critiche, mentre gli errori personalizzati ti consentono di gestire scenari specifici con eleganza. Mettiti alla prova con gli esercizi proposti per padroneggiare queste tecniche e scrivere codice robusto in Rust! 




Commenti
* Obbligatorio