Rust si basa su una gestione esplicita degli errori: le eccezioni sono assenti, invece viene utilizzato un valore di ritorno Result<T, E>. Questo garantisce la sicurezza e la prevedibilità del codice.
Storia della questione:
Molti linguaggi hanno seguito la strada delle eccezioni, il che ha portato a situazioni inaspettate e alla necessità di gestire esplicitamente le eccezioni a runtime. Rust fin dall'inizio ha puntato sul controllo tramite tipi, tutti gli errori devono essere parte della firma della funzione.
Problema:
La sfida principale è scrivere codice in cui gli errori non vengano ignorati o mascherati, senza funzioni che "cadono", ma mantenendo il codice compatto e leggibile. È necessario propagare correttamente gli errori a un livello superiore, senza perdere informazioni sul loro tipo e senza confondere la logica.
Soluzione:
Lo strumento chiave è il tipo Result<T, E> e l'operatore ?, che "espande" automaticamente il risultato: in caso di errore, avviene un'uscita immediata dalla funzione con il ritorno dell'errore, e in caso di successo viene restituito il valore.
Esempio di codice:
fn read_number(file: &str) -> Result<i32, std::io::Error> { let content = std::fs::read_to_string(file)?; let num: i32 = content.trim().parse()?; Ok(num) }
Caratteristiche chiave:
.map_err() e altri metodi)È possibile utilizzare ? nelle funzioni che restituiscono Unit (void)?
No, l'operatore ? è consentito solo all'interno di funzioni che restituiscono Result o Option. Se la funzione restituisce (), non è possibile usare ?.
Cosa succede se i tipi di errore in più chiamate con ? sono diversi?
Si ottiene un errore di compilazione: il tipo di errore deve essere chiaramente definito. È necessario unificare tutti gli errori in un tipo unico tramite .map_err() oppure usare thiserror, o descrivere un enum-wrapper a livello API. Esempio:
fn foo() -> Result<_, MyError> { let a = bar()?; let b = baz().map_err(MyError::Baz)?; Ok(…) }
Qual è il problema di .unwrap() nella logica interna?
Esiste una comune idea sbagliata che nel codice "principale" sia possibile utilizzare liberamente unwrap(), "non fallirà di certo". In realtà, anche un piccolo errore invisibile porterà a un panico a runtime — compromettendo la sicurezza del programma.
unwrap/expect, soprattutto nella logica internaNel codice di produzione sono rimasti molti unwrap() dopo una rapida fase di debugging. Di conseguenza, l'applicazione è crollata in caso di errori di parsing dovuti a input non validi dell'utente.
Pro:
Contro:
È stato utilizzato un intero stack Result<T, E> con una descrizione esplicita degli errori e sono stati impiegati solo ? e .map_err(), mantenendo tutte le informazioni sul guasto.
Pro:
Contro: