Hintergrund:
Go unterscheidet sich von vielen anderen Sprachen durch seinen Ansatz zum Fehlerhandling, bei dem Fehler Werte und keine Ausnahmen sind. Dieses Design wurde wegen seiner Transparenz und Einfachheit gewählt: Jedes Mal, wenn etwas schief gehen kann, gibt die Funktion den Fehler explizit als zweites Rückgabewert zurück.
Problem:
Viele Anfänger versuchen, die gewohnten Try-Catch-Muster auf Go anzuwenden, verlieren sich in der Vielzahl der Fehlerprüfungen oder verwenden das Wrapping von Fehlern zur Übermittlung von Kontext nicht. Das führt zu einem Verlust an Informationsgehalt und zu schwieriger Fehlersuche.
Lösung:
In Go wird eine Funktion, die einen Fehler zurückgeben kann, folgendermaßen deklariert:
func doSomething() (ResultType, error) { // ... if somethingWrong { return nil, errors.New("es ist etwas schief gelaufen") } return result, nil }
Die Fehlerprüfung liegt in der Verantwortung der aufrufenden Stelle:
res, err := doSomething() if err != nil { log.Fatalf("Prozess fehlgeschlagen: %w", err) }
Go 1.13 und höher erlauben das "Wrappen" von Fehlern mit fmt.Errorf("%w", err), um Fehlerketten zu erstellen. Dies verbessert die Diagnose und fördert die beste Praxis der kontextbezogenen Beschreibung von Fehlern.
Hauptmerkmale:
Warum eigene Fehlerarten erstellen, wenn man einfach errors.New("...") zurückgeben kann?
Die richtige Antwort: Eigene Fehlerarten ermöglichen nicht nur die Mitteilung des Fehltextes, sondern auch die Aufbewahrung zusätzlicher Informationen für die spätere Verarbeitung (z.B. Rückgabecodes, Kontext) und die Umsetzung eines differenzierten Handlings durch Type Assertion.
Beispiel:
type NotFoundError struct { Resource string } func (e NotFoundError) Error() string { return fmt.Sprintf("%s nicht gefunden", e.Resource) } // Überprüfung if _, ok := err.(NotFoundError); ok { // Fehlerbehandlung bei Suche }
Ist panic ein guter Ersatz für Fehler bei kritischen Fehlern?
Nein, panic wird in Go nur in wirklich auswegslosen Situationen angewendet – beispielsweise bei Fehlern in der Programmlogik (programmspezifische Invarianten), jedoch nicht für normale Fehler (z.B. wenn eine Datei nicht geöffnet werden kann). Die Verwendung von panic zur Signalisierung routinemäßiger Fehler ist falsch und führt zu unleserlichem, unkontrollierbarem Code.
Was passiert, wenn die Fehlerverarbeitung (err) in verschachtelten Funktionen übersprungen wird?
Der Fehler "geht verloren", der Code wird weiterhin ausgeführt, was zu einer Verbreitung des fehlerhaften Zustands führen kann. Es ist immer wichtig, jeden Fehler-Rückgabewert korrekt zu behandeln.
_ = ...Jede Funktion gibt einfach errors.New(...), die Fehler sind nicht gewrappt, die Fehlerarten sind unterschiedlich, aber die Behandlung ist immer die gleiche – sie wird geloggt und verworfen. Infolgedessen sind die Log-Dateien mit nicht informativen Nachrichten gefüllt, die nicht bis zur ursprünglichen Ursache zurückverfolgt werden können.
Vorteile:
Nachteile:
Es werden Fehlerwrapping mit fmt.Errorf("%w", err), eigene Fehler mit nützlichen Feldern und Überprüfungen durch errors.Is()/errors.As() verwendet. Dies ermöglicht es, mit Details zu protokollieren, Geschäftsfehler von Umweltfehlern zu trennen und eine zuverlässige Retry-Logik zu schreiben.
Vorteile:
Nachteile: