errors.Is() und errors.As()
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 56 von 107.
Wenn Fehler verpackt werden, funktionieren einfache Gleichheitsprüfungen wie err == ErrNotFound nicht mehr. Das errors-Paket bietet zwei Funktionen zur Untersuchung von verpackten Fehlerketten an: errors.Is() und errors.As().
errors.Is() prüft, ob ein Fehler in der Kette mit einem bestimmten Sentinel-Fehler übereinstimmt:
import "errors"
var ErrNotFound = errors.New("not found")
func FindUser(id int) error {
return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}
err := FindUser(42)
if errors.Is(err, ErrNotFound) {
fmt.Println("User doesn't exist")
}Obwohl der zurückgegebene Fehler eingepackt (wrapped) ist, entpackt errors.Is() die Kette und findet ErrNotFound darin.
errors.As() prüft, ob ein Fehler in der Kette mit einem bestimmten Typ übereinstimmt und extrahiert diesen:
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("Invalid field:", ve.Field)
}Verwenden Sie errors.Is() beim Prüfen auf Sentinel-Fehler und errors.As(), wenn Sie auf Felder eines benutzerdefinierten Fehlertyps zugreifen müssen. Beide Funktionen durchlaufen die gesamte Fehlerkette, was sie für die korrekte Behandlung von gewrappten Fehlern (wrapped errors) unerlässlich macht.
Aufgabe
EinfachLassen Sie uns ein Dateiverarbeitungssystem erstellen, das demonstriert, wie man verschachtelte Fehlerketten (wrapped error chains) mit errors.Is() und errors.As() korrekt inspiziert. Sie werden Fehler erstellen, die beim Durchlaufen verschiedener Ebenen eingepackt (wrapped) werden, und dann diese Funktionen verwenden, um sie zu identifizieren und Informationen daraus zu extrahieren.
Sie werden Ihren Code in zwei Dateien organisieren:
errors.go: Definieren Sie die Fehler-Infrastruktur für Ihr Dateisystem.Erstellen Sie einen Sentinel-Fehler:
ErrPermissionDeniedmit der Nachricht"permission denied"
Erstellen Sie einen benutzerdefinierten Fehlertyp:
FileErrorStruct mit den FeldernFilename(string) undOperation(string)- Die Methode
Error()sollte Folgendes zurückgeben:[Operation] failed on file: [Filename]
Implementieren Sie zwei Funktionen, die Dateioperationen simulieren und Fehler einpacken:
ReadFile(filename string) error- Wenn der Dateiname"secret.txt"ist, geben SieErrPermissionDeniedzurück. Wenn der Dateiname"missing.txt"ist, geben Sie einen*FileErrormit der Operation"read"und dem angegebenen Dateinamen zurück. Andernfalls geben Sienilzurück.ProcessFile(filename string) error- RuftReadFileauf. Wenn ein Fehler zurückgegeben wird, packen Sie ihn mitfmt.Errorfund dem Format"processing failed: %w"ein. Andernfalls geben Sienilzurück.
main.go: Lesen Sie einen Dateinamen von der Eingabe ein und rufen SieProcessFileauf. Verwenden Sieerrors.Is()underrors.As(), um die verschachtelte Fehlerkette zu inspizieren und basierend auf dem Inhalt entsprechende Nachrichten auszugeben.
Die folgende Eingabe wird bereitgestellt:
- Zeile 1: Zu verarbeitender Dateiname
Behandeln Sie das Ergebnis, indem Sie die Fehlerkette inspizieren:
- Wenn
errors.Is()den FehlerErrPermissionDeniedin der Kette findet: Geben Sie die vollständige Fehlermeldung aus und in einer neuen ZeileAccess denied - check file permissions - Wenn
errors.As()einen*FileErrorin der Kette findet: Geben Sie die vollständige Fehlermeldung aus und in einer neuen ZeileFile issue: [Filename] during [Operation] - Wenn kein Fehler auftritt: Geben Sie
File '[filename]' processed successfullyaus
Zum Beispiel sollte Ihre Ausgabe bei secret.txt wie folgt aussehen:
processing failed: permission denied
Access denied - check file permissionsUnd bei missing.txt sollte Ihre Ausgabe wie folgt aussehen:
processing failed: read failed on file: missing.txt
File issue: missing.txt during readUnd bei data.txt sollte Ihre Ausgabe wie folgt aussehen:
File 'data.txt' processed successfullyBeachten Sie, dass Sie, obwohl die Fehler von ProcessFile eingepackt wurden, den ursprünglichen Sentinel-Fehler mit errors.Is() erkennen und den benutzerdefinierten Fehlertyp mit errors.As() extrahieren können. Diese Funktionen durchlaufen die gesamte Fehlerkette, um das Gesuchte zu finden.
Spickzettel
Wenn Fehler verschachtelt (wrapped) sind, verwenden Sie errors.Is() und errors.As() aus dem errors-Paket, um die Fehlerkette zu untersuchen.
errors.Is() prüft, ob ein Fehler in der Kette mit einem Sentinel-Fehler übereinstimmt:
import "errors"
var ErrNotFound = errors.New("not found")
err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
// ErrNotFound wurde in der Kette gefunden
}errors.As() prüft, ob ein Fehler in der Kette einem bestimmten Typ entspricht, und extrahiert diesen:
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
// ve enthält nun den ValidationError aus der Kette
fmt.Println(ve.Field)
}Verwenden Sie errors.Is() für Sentinel-Fehler und errors.As(), wenn Sie auf Felder von benutzerdefinierten Fehlertypen zugreifen müssen. Beide Funktionen durchlaufen die gesamte Fehlerkette.
Probier es selbst
package main
import (
"errors"
"fmt"
)
func main() {
// Dateinamen von der Eingabe lesen
var filename string
fmt.Scanln(&filename)
// ProcessFile mit dem Dateinamen aufrufen
err := ProcessFile(filename)
// TODO: Das Ergebnis durch Überprüfung der Fehlerkette verarbeiten
//
// Verwende errors.Is(), um zu prüfen, ob ErrPermissionDenied in der Kette enthalten ist:
// - Wenn gefunden: Den vollständigen Fehler ausgeben, dann "Access denied - check file permissions"
//
// Verwende errors.As(), um zu prüfen, ob ein *FileError in der Kette enthalten ist:
// - Wenn gefunden: Den vollständigen Fehler ausgeben, dann "File issue: [Filename] during [Operation]"
//
// Wenn kein Fehler: "File '[filename]' processed successfully" ausgeben
_ = errors.Is // Hinweis: errors.Is() verwenden
_ = errors.As // Hinweis: errors.As() verwenden
_ = err
}
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