Historie der Frage:
Vor Go 1.13 war ein Fehler einfach ein Interface. Um zusätzlichen Kontext zu einem Fehler zu geben, wurden oft eigene Typen erstellt, aber es gab kein strukturiertes Mechanismus für das Einbetten, wie in modernem Java oder C#. Mit der Einführung von Go 1.13 wurde eine standardisierte Methode zum Wrapping (Einbetten) von Fehlern über die Funktionen fmt.Errorf und zur Ermittlung der Wurzelursache (errors.Is/errors.As) eingeführt.
Problem:
In komplexen Anwendungen, in denen Fehler auf verschiedenen Ebenen des Stacks auftreten können, ist es wichtig, den Kontext beim Zurückgeben eines Fehlers aus der Tiefe des Codes nicht zu verlieren. Andernfalls wird das Debuggen schwierig, und es ist unmöglich zu verstehen, wo in der Kette der Fehler aufgetreten ist.
Lösung:
Go bietet an, Fehler in neuen Fehlerobjekten zu umschließen, die die Ursache enthalten. Dazu wird %w in fmt.Errorf verwendet, und zur Überprüfung tieferliegender Ursachen — errors.Is oder errors.As aus dem Package errors.
Beispielcode:
import ( "errors" "fmt" ) var ErrNotFound = errors.New("nicht gefunden") func getData() error { return fmt.Errorf("Dienstdatenbank: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("Ursache erkannt: nicht gefunden") } else { fmt.Println("Anderer Fehler:", err) } }
Wichtige Merkmale:
%w zum Einbetten von Fehlern über fmt.Errorf.errors.Is und errors.As.Welcher Formatierungsoperator wird benötigt, um Fehler über fmt.Errorf zu umschließen?
Es sollte %w verwendet werden, nicht %v — nur %w unterstützt das Entpacken.
Beispielcode:
fmt.Errorf("Fehler: %w", err)
Kann man Fehlerketten manuell ohne fmt.Errorf erstellen und dennoch über errors.Is erkennen?
Nein, man muss das Unwrap-Interface implementieren, sonst entpacken die Standardfunktionen die Kette nicht.
Beispielcode für das Interface:
type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }
Geben Fehler nil-Werte zurück, nachdem sie umschlossen wurden, wenn der eingebettete Fehler nil war?
Nein, wenn der eingebettete Fehler nil ist, ist der umschlossene Fehler nur dann nil, wenn er korrekt verwendet wird. Wenn jedoch eine Wrapper-Struktur manuell erstellt wird, in der das Fehlerfeld nil ist, wird es nicht nil sein. Dies führt häufig zu Verwirrung bei der Überprüfung.
%w nicht verwenden, sondern nur %v — die Möglichkeit zur Analyse der Kette geht verloren.Unwrap() für eigene Fehlerstrukturen nicht implementieren.errors.Is/As durchführen, sondern nur den Fehler direkt vergleichen.In der Anwendung wurde einfach bei jedem Level ein neuer Fehler mit Text zurückgegeben:
return errors.New("Datenbank Schreibfehler")
Vorteile:
Nachteile:
Umschließung von Fehlern mit %w und Analyse über errors.Is verwendet:
if errors.Is(err, ErrNotFound) { return fmt.Errorf("Fehler auf Servicestufe: %w", err) }
Vorteile:
Nachteile: