ProgrammazioneSviluppatore Backend

Parla di come funziona la gestione degli errori tramite il package error wrapping in Go: cos'è l'error wrapping, perché è importante e come implementare correttamente l'incapsulamento degli errori?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della questione:

Fino a Go 1.13, gli errori erano semplici interfacce. Per ulteriori dettagli, si creavano spesso tipi di errori personalizzati, ma non c'era un meccanismo strutturato di incapsulamento, come in Java moderno o C#. Con l'introduzione di Go 1.13 è stato introdotto un modo standard di avvolgere (wrapping) gli errori tramite la funzione fmt.Errorf e di individuare la causa radice (errors.Is/errors.As).

Problema:

In applicazioni complesse, dove gli errori possono sorgere a livelli diversi dello stack, è importante non perdere il contesto quando si restituisce un errore dal profondo del codice. Altrimenti, il debug diventa difficile e non è possibile capire dove nella catena sia avvenuto il fallimento.

Soluzione:

Go offre la possibilità di avvolgere gli errori in nuovi oggetti di errore che contengono la causa. A tal fine, si utilizza %w in fmt.Errorf, mentre per controllare le cause profonde si usano errors.Is o errors.As dal package errors.

Esempio di codice:

import ( "errors" "fmt" ) var ErrNotFound = errors.New("non trovato") func getData() error { return fmt.Errorf("errore nel database del servizio: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("Individuata la causa: non trovato") } else { fmt.Println("Altro errore:", err) } }

Caratteristiche chiave:

  • Uso di %w per incapsulare errori tramite fmt.Errorf.
  • Individuazione delle cause incapsulate mediante errors.Is e errors.As.
  • Compatibilità con tipi di errore personalizzati durante l'incapsulamento e il disimballaggio.

Domande trabocchetto.

Quale operatore di formattazione è necessario per l'incapsulamento degli errori tramite fmt.Errorf?

È corretto utilizzare %w, e non %v — solo %w supporta il disimballaggio.

Esempio di codice:

fmt.Errorf("errore: %w", err)

È possibile creare manualmente catene di errori senza fmt.Errorf e comunque identificarle tramite errors.Is?

No, è necessario implementare l'interfaccia Unwrap, altrimenti le funzioni standard non disimballeranno la catena.

Esempio di codice dell'interfaccia:

type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }

Restituiscono gli errori valore nil dopo il wrapping, se l'errore incapsulato era nil?

No, se l'errore incapsulato è nil, allora l'errore avvolto è nil solo se utilizzato correttamente. Ma se si crea manualmente una struttura wrapper dove il campo errore è nil, non sarà nil. Questo porta spesso a confusione durante il controllo.

Errori comuni e antipattern

  • Non usare %w, ma solo %v — si perde la possibilità di analizzare la catena.
  • Non implementare Unwrap() per le proprie strutture di errore.
  • Non controllare tramite errors.Is/As, ma solo confrontare l'errore direttamente.
  • Creazione di nuovi errori senza causa (perdita di contesto).

Esempio dalla vita reale

Caso negativo

Nell'applicazione veniva semplicemente restituito un nuovo errore con testo a ogni livello:

return errors.New("errore di scrittura nel DB")

Vantaggi:

  • Semplice e veloce.

Svantaggi:

  • Impossibile distinguere che l'errore era in realtà "non trovato" dal profondo, compromettendo la logica di business sopra.
  • Perdita di informazioni sulla pila e sulla fonte dell'errore.

Caso positivo

È stato utilizzato il wrapping degli errori con %w e l'analisi tramite errors.Is:

if errors.Is(err, ErrNotFound) { return fmt.Errorf("errore livello servizio: %w", err) }

Vantaggi:

  • Viene correttamente determinata la causa a qualsiasi livello.
  • Più facile da debuggare, è sempre visibile il contesto originale.

Svantaggi:

  • Richiede la comprensione di come funziona il wrapping e una scrittura qualificata degli errori.