Wrapping degli errori (fmt.Errorf)
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 54 di 107.
Quando gli errori attraversano più livelli della tua applicazione, sapere da dove ha avuto origine un errore diventa fondamentale. La funzione fmt.Errorf di Go con il verbo %w ti consente di avvolgere gli errori, preservando l’errore originale e aggiungendo contesto.
L’incapsulamento degli errori crea una catena di errori. Ogni livello può aggiungere informazioni su ciò che stava cercando di fare quando si è verificato l’errore:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// elabora i dati...
return nil
}Il verbo %w è speciale. Racchiude l'errore originale all'interno di quello nuovo. È diverso da %v, che si limita a convertire l'errore in una stringa e fa perdere l'identità dell'errore originale.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Quando LoadSettings fallisce, il messaggio di errore mostra l’intera catena: "loading settings: reading config file: open settings.json: no such file or directory". Ogni livello aggiunge contesto, rendendo il debug molto più semplice.
L’errore avvolto contiene ancora l’errore originale al suo interno. Nella prossima lezione imparerai a svolgere questi errori e a controllare cosa contengono usando errors.Is() e errors.As().
Sfida
FacileCostruiamo un sistema di elaborazione degli ordini che dimostri come avvolgere gli errori attraverso più livelli. Creerai una catena di funzioni in cui ogni livello aggiunge contesto agli errori, rendendo facile individuare esattamente dove si verificano i problemi.
Organizzerai il codice in due file:
orders.go: crea la logica di elaborazione degli ordini con più livelli che avvolgono gli errori mentre risalgono la catena.Implementa tre funzioni che formano una catena di elaborazione:
ValidateItem(itemID string) error- Restituisce un errore con il messaggio"item not found"se itemID è"INVALID", altrimenti restituiscenilProcessOrder(orderID, itemID string) error- ChiamaValidateItem. Se restituisce un errore, avvolgilo con il contesto:"processing order [orderID]: %w". Altrimenti restituiscinilSubmitOrder(customerName, orderID, itemID string) error- ChiamaProcessOrder. Se restituisce un errore, avvolgilo con il contesto:"submitting order for [customerName]: %w". Altrimenti restituiscinil
Ogni livello dovrebbe usare
fmt.Errorfcon il verbo%wper avvolgere l'errore del livello sottostante, creando una catena di contesto.main.go: leggi i dettagli dell'ordine dall'input, chiamaSubmitOrdere mostra il risultato. Se si verifica un errore, stampa l'intera catena di errori. Se l'operazione riesce, stampa un messaggio di conferma.
Verranno forniti i seguenti input:
- Riga 1: nome del cliente
- Riga 2: ID dell'ordine
- Riga 3: ID dell'articolo
Stampa il risultato:
- Se si verifica un errore:
Error: [full error chain] - Se l'operazione riesce:
Order [orderID] submitted successfully for [customerName]
Ad esempio, con Alice, ORD-123 e INVALID, l'output dovrebbe essere:
Error: submitting order for Alice: processing order ORD-123: item not foundNota come il messaggio di errore mostri la catena completa. Puoi risalire al problema dal tentativo di invio di livello più alto, passando per l'elaborazione dell'ordine, fino all'effettivo errore di convalida. Ogni livello ha aggiunto il proprio contesto usando %w.
E con Bob, ORD-456 e ITEM-001, l'output dovrebbe essere:
Order ORD-456 submitted successfully for BobProvalo tu
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leggi il nome del cliente
scanner.Scan()
customerName := scanner.Text()
// Leggi l'ID dell'ordine
scanner.Scan()
orderID := scanner.Text()
// Leggi l'ID dell'articolo
scanner.Scan()
itemID := scanner.Text()
// TODO: Chiama SubmitOrder con i valori di input
// TODO: Se si verifica un errore, stampa: Error: [full error chain]
// TODO: Se l'operazione riesce, stampa: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online