In Go, l'approccio standard per la gestione degli errori consiste nel restituire un valore di tipo error dalla funzione. A differenza dei linguaggi con eccezioni, in Go le eccezioni (panic, recover) vengono utilizzate solo per situazioni veramente eccezionali; per la logica di business si dovrebbero utilizzare errori normali.
Il modo classico:
func doSomething() error { // ... return nil // o errors.New("some error") } err := doSomething() if err != nil { // gestione dell'errore }
È possibile creare propri tipi di errore per un'analisi più dettagliata:
type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf("(%d) %s", e.Code, e.Message) } err := &MyError{404, "not found"}
Controllo tipico di un errore specifico:
if myErr, ok := err.(*MyError); ok && myErr.Code == 404 { // gestione dell'errore 404 }
È possibile confrontare error con nil usando == in Go?
Risposta: Sì, è possibile e necessario, per controllare l'assenza di errori. Ma è importante ricordare: se l'errore è stato avvolto o creato usando errori che contengono nil all'interno delle strutture, il confronto potrebbe essere errato. Ad esempio:
var err error = (*MyError)(nil) fmt.Println(err == nil) // false!
L'errore si verifica perché l'interfaccia non è uguale a nil, se il suo tipo non è nil, anche se il valore interno è nil.
Storia
Il microservizio era impegnato nella registrazione degli errori mediante il confronto con nil. Uno degli sviluppatori restituiva un errore tipizzato come (*MyError)(nil), non rendendosi conto che questo non è equivalente a nil (l'interfaccia non è nil). Di conseguenza, la gestione degli errori ha smesso di funzionare, e molte false allerta sono finite nelle metriche.
Storia
Nel progetto, invece di passare l'errore a catena, veniva usato panic per tutti gli errori, ritenendo che fosse un modo "pulito". Questo ha portato al crash dell'applicazione ad ogni input non valido dell'utente, poiché panic/recover non sono destinati alla logica di business continua.
Storia
L'errore è stato avvolto più volte usando fmt.Errorf("some: %w", err), ma durante il controllo su un tipo specifico di errore veniva effettuato un confronto normale, invece di utilizzare errors.Is e errors.As. Questo portava al fatto che la logica di business non reagiva ai tipi di errore personalizzati, anche se erano realmente presenti all'interno dell'errore avvolto.