Storia del problema:
Go si è distinto da molti linguaggi per il suo approccio alla gestione degli errori, dove gli errori sono valori e non eccezioni. Questo design è stato scelto per trasparenza e semplicità: ogni volta che qualcosa può andare storto, una funzione restituisce esplicitamente un errore come secondo valore restituito.
Problema:
Molti principianti cercano di applicare a Go i modelli familiari try-catch, si perdono nel grande numero di controlli degli errori o non usano il wrapping degli errori per trasmettere il contesto. Questo porta a una perdita di informatività e a un debug difficile.
Soluzione:
In Go, una funzione che può restituire un errore è dichiarata così:
func doSomething() (ResultType, error) { // ... if somethingWrong { return nil, errors.New("qualcosa è andato storto") } return result, nil }
Il controllo degli errori è responsabilità della parte chiamante:
res, err := doSomething() if err != nil { log.Fatalf("processo fallito: %w", err) }
Go 1.13 e superiore consente di "wrappare" gli errori usando fmt.Errorf("%w", err) per costruire catene di errori. Questo migliora la diagnosi e promuove le best practice di descrizione contestuale degli errori.
Caratteristiche chiave:
Perché creare i propri tipi di errori se è possibile semplicemente restituire errors.New("...")?
La risposta corretta: i propri tipi di errori consentono non solo di comunicare il testo dell'errore, ma anche di conservare informazioni aggiuntive per ulteriori elaborazioni (ad esempio, codici di ritorno, contesto), oltre a implementare una manipolazione più fine tramite type assertion.
Esempio:
type NotFoundError struct { Resource string } func (e NotFoundError) Error() string { return fmt.Sprintf("%s non trovato", e.Resource) } // Controllo if _, ok := err.(NotFoundError); ok { // gestione dell'errore di ricerca }
È panic una buona sostituzione per error per errori critici?
No, panic in Go è usato solo in situazioni davvero disperate — ad esempio, in caso di errori della stessa programmazione (invarianti di programma), ma non per guasti normali (ad esempio, impossibile aprire un file). Utilizzare panic per segnalare errori di routine è sbagliato e porta a codice illeggibile e non gestibile.
Cosa succede se si salta la gestione dell'errore (err) nelle funzioni annidate?
L'errore viene "perso", il codice continua a essere eseguito, il che può portare alla diffusione di uno stato errato. È sempre importante gestire correttamente ogni ritorno di errore.
_ = ...Ogni funzione restituisce semplicemente errors.New(...), gli errori non sono wrappati, i tipi di errore sono diversi, ma la gestione è sempre la stessa — viene registrata ed esce. Di conseguenza, i file di log sono pieni di messaggi non informativi, impossibili da rintracciare fino alla causa originale.
Vantaggi:
Svantaggi:
Si utilizzano wrapping degli errori tramite fmt.Errorf("%w", err), errori propri con campi utili, controlli tramite errors.Is()/errors.As(). Questo consente di registrare con dettagli, separare gli errori di business dai guasti ambientali e scrivere logiche di retry affidabili.
Vantaggi:
Svantaggi: