Menu
Coddy logo textTech

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 Fehlerkette 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 umschlossen ist, entpackt errors.Is() die Kette und findet darin ErrNotFound.

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)
}

Verwende errors.Is() beim Prüfen auf Sentinel-Fehler und errors.As(), wenn du auf Felder eines benutzerdefinierten Fehlertyps zugreifen musst. Beide Funktionen durchlaufen die gesamte Fehlerkette, wodurch sie für die korrekte Behandlung umschlossener Fehler unerlässlich sind.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Dateiverarbeitungssystem, das demonstriert, wie man eingeschlossene Fehlerketten mit errors.Is() und errors.As() ordnungsgemäß untersucht. Du wirst Fehler erstellen, die beim Durchlaufen verschiedener Schichten eingeschlossen werden, und anschließend diese Funktionen verwenden, um Informationen aus ihnen zu identifizieren und zu extrahieren.

Du wirst deinen Code auf zwei Dateien verteilen:

  • errors.go: Definiere die Fehlerinfrastruktur für dein Dateisystem.

    Erstelle einen Sentinel-Fehler:

    • ErrPermissionDenied mit der Meldung "permission denied"

    Erstelle einen benutzerdefinierten Fehlertyp:

    • Eine FileError-Struktur mit den Feldern Filename (string) und Operation (string)
    • Ihre Error()-Methode soll Folgendes zurückgeben: [Operation] failed on file: [Filename]

    Implementiere zwei Funktionen, die Dateioperationen simulieren und Fehler einschließen:

    • ReadFile(filename string) error – Wenn filename "secret.txt" ist, gib ErrPermissionDenied zurück. Wenn filename "missing.txt" ist, gib einen *FileError mit der Operation "read" und dem angegebenen Filename zurück. Andernfalls gib nil zurück.
    • ProcessFile(filename string) error – Ruft ReadFile auf. Wenn ein Fehler zurückgegeben wird, schließe ihn mit fmt.Errorf und dem Format "processing failed: %w" ein. Andernfalls gib nil zurück.
  • main.go: Lies einen Dateinamen aus der Eingabe und rufe ProcessFile auf. Verwende errors.Is() und errors.As(), um die eingeschlossene Fehlerkette zu untersuchen und abhängig davon, was du darin findest, passende Meldungen auszugeben.

Die folgende Eingabe wird bereitgestellt:

  • Zeile 1: Zu verarbeitender Dateiname

Verarbeite das Ergebnis, indem du die Fehlerkette untersuchst:

  • Wenn errors.Is() ErrPermissionDenied in der Kette findet: Gib die vollständige Fehlermeldung aus und in einer neuen Zeile anschließend Access denied - check file permissions
  • Wenn errors.As() einen *FileError in der Kette findet: Gib die vollständige Fehlermeldung aus und in einer neuen Zeile anschließend File issue: [Filename] during [Operation]
  • Wenn kein Fehler auftritt: Gib File '[filename]' processed successfully aus

Wenn beispielsweise secret.txt angegeben wird, sollte deine Ausgabe wie folgt aussehen:

processing failed: permission denied
Access denied - check file permissions

Und wenn missing.txt angegeben wird, sollte deine Ausgabe wie folgt aussehen:

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

Und wenn data.txt angegeben wird, sollte deine Ausgabe wie folgt aussehen:

File 'data.txt' processed successfully

Beachte, dass du den ursprünglichen Sentinel-Fehler mit ProcessFile weiterhin erkennen und den benutzerdefinierten Fehlertyp mit errors.Is() extrahieren kannst, obwohl die Fehler von errors.As() eingeschlossen werden. Diese Funktionen durchlaufen die gesamte Fehlerkette, um das zu finden, wonach du suchst.

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
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Go-Compiler