Error Wrapping (fmt.Errorf)
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 54 von 107.
Wenn Fehler mehrere Schichten Ihrer Anwendung durchlaufen, wird es entscheidend zu wissen, wo ein Fehler seinen Ursprung hat. Die fmt.Errorf-Funktion von Go mit dem Verb %w ermöglicht es Ihnen, Fehler zu verpacken, wodurch der ursprüngliche Fehler erhalten bleibt, während Kontext hinzugefügt wird.
Error Wrapping erzeugt eine Kette von Fehlern. Jede Ebene kann Informationen darüber hinzufügen, was sie gerade versuchte, als der Fehler auftrat:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// Daten verarbeiten...
return nil
}Das %w-Verb ist speziell – es verpackt den ursprünglichen Fehler innerhalb des neuen. Dies unterscheidet sich von %v, das den Fehler lediglich in einen String umwandelt und die Identität des ursprünglichen Fehlers verliert.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Wenn LoadSettings fehlschlägt, zeigt die Fehlermeldung die vollständige Kette an: "loading settings: reading config file: open settings.json: no such file or directory". Jede Ebene fügt Kontext hinzu, was das Debugging erheblich erleichtert.
Der umschlossene Fehler enthält immer noch den ursprünglichen Fehler in seinem Inneren. In der nächsten Lektion lernst du, wie du diese Fehler entpackst und mit errors.Is() und errors.As() überprüfst, was sich darin befindet.
Aufgabe
EinfachLassen Sie uns ein Auftragsverarbeitungssystem erstellen, das das Error Wrapping über mehrere Ebenen hinweg demonstriert. Sie werden eine Kette von Funktionen erstellen, bei der jede Ebene Kontext zu Fehlern hinzufügt, was es einfach macht, genau zu verfolgen, wo Probleme auftreten.
Sie werden Ihren Code auf zwei Dateien verteilen:
orders.go: Erstellen Sie die Logik zur Auftragsverarbeitung mit mehreren Ebenen, die Fehler beim Aufsteigen in der Hierarchie einpacken (wrap).Implementieren Sie drei Funktionen, die eine Verarbeitungskette bilden:
ValidateItem(itemID string) error- Gibt einen Fehler mit der Nachricht"item not found"zurück, wenn die itemID"INVALID"ist, andernfalls wirdnilzurückgegeben.ProcessOrder(orderID, itemID string) error- RuftValidateItemauf. Wenn ein Fehler zurückgegeben wird, verpacken Sie diesen mit dem Kontext:"processing order [orderID]: %w". Andernfalls geben Sienilzurück.SubmitOrder(customerName, orderID, itemID string) error- RuftProcessOrderauf. Wenn ein Fehler zurückgegeben wird, verpacken Sie diesen mit dem Kontext:"submitting order for [customerName]: %w". Andernfalls geben Sienilzurück.
Jede Ebene sollte
fmt.Errorfmit dem Verb%wverwenden, um den Fehler der darunter liegenden Ebene einzupacken und so eine Kette von Kontexten aufzubauen.main.go: Lesen Sie die Bestelldetails aus der Eingabe ein, rufen SieSubmitOrderauf und zeigen Sie das Ergebnis an. Wenn ein Fehler auftritt, geben Sie die vollständige Fehlerkette aus. Wenn der Vorgang erfolgreich ist, geben Sie eine Bestätigungsmeldung aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Kundenname
- Zeile 2: Bestell-ID
- Zeile 3: Artikel-ID
Geben Sie das Ergebnis aus:
- Wenn ein Fehler auftritt:
Error: [full error chain] - Bei Erfolg:
Order [orderID] submitted successfully for [customerName]
Zum Beispiel, gegeben Alice, ORD-123 und INVALID, sollte Ihre Ausgabe wie folgt aussehen:
Error: submitting order for Alice: processing order ORD-123: item not foundBeachten Sie, wie die Fehlermeldung die vollständige Kette anzeigt – Sie können das Problem vom Übermittlungsversuch auf oberster Ebene über die Auftragsverarbeitung bis hin zum eigentlichen Validierungsfehler zurückverfolgen. Jede Ebene hat ihren eigenen Kontext mit %w hinzugefügt.
Und gegeben Bob, ORD-456 und ITEM-001, sollte Ihre Ausgabe wie folgt aussehen:
Order ORD-456 submitted successfully for BobSpickzettel
Verwenden Sie fmt.Errorf mit dem Verb %w, um Fehler zu wrappen und dabei den ursprünglichen Fehler beizubehalten:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
return nil
}Das Verb %w wrappt den ursprünglichen Fehler innerhalb des neuen, im Gegensatz zu %v, das den Fehler nur in einen String umwandelt und die Identität des ursprünglichen Fehlers verliert.
Error-Wrapping erzeugt eine Kette, in der jede Ebene Kontext hinzufügt:
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Die resultierende Fehlermeldung zeigt die vollständige Kette: "loading settings: reading config file: open settings.json: no such file or directory"
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Kundennamen lesen
scanner.Scan()
customerName := scanner.Text()
// Bestell-ID lesen
scanner.Scan()
orderID := scanner.Text()
// Artikel-ID lesen
scanner.Scan()
itemID := scanner.Text()
// TODO: SubmitOrder mit den Eingabewerten aufrufen
// TODO: Wenn ein Fehler auftritt, ausgeben: Error: [full error chain]
// TODO: Wenn erfolgreich, ausgeben: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool