Direkt zum Hauptinhalt springen
eLearner.app
Modul 10 · Lektion 2 von 547/50 im Kurs~14 min
Lektionen des Moduls (2/5)

Idiomatische Fehlerbehandlung

Die Fehlerbehandlung in Go ist explizit: keine Ausnahmen, kein Try/Catch. Jede Funktion, die fehlschlagen kann, gibt error als letzten Wert zurück, und der Aufrufer entscheidet. In diesem Kapitel werden die idiomatischen Muster gesammelt, um Fehler auf robuste Weise zu erzeugen, zu verbreiten und zu prüfen.

Die goldene Regel

Go
data, err := os.ReadFile(path)
if err != nil {
    return fmt.Errorf("config %s: %w", path, err)
}

Vier Prinzipien:

  1. error ist die letzte Rückgabe.
  2. Sofort prüfen mit if err != nil.
  3. Kontext hinzufügen vor der Weitergabe (mit fmt.Errorf + %w).
  4. Ignorieren Sie niemals einen Fehler: Behandeln Sie ihn entweder, protokollieren Sie ihn oder geben Sie ihn zurück.
Go
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)

Sentinel-Fehler

Ein Sentinel-Fehler ist eine exportierte globale Variable var ErrXxx = errors.New("..."), mit der der Aufrufer vergleichen kann:

Go
package store

var ErrNotFound = errors.New("not found")

func Get(key string) (string, error) {
    if !exists(key) {
        return "", ErrNotFound
    }
    // ...
}

Der Aufrufer verwendet errors.Is (NICHT ==), der die Wrap-Kette durchläuft:

Go
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
    // handle 404
}

Standardbeispiele: io.EOF, sql.ErrNoRows, os.ErrNotExist.

Benutzerdefinierte eingegebene Fehler

Um strukturierte Daten (HTTP-Code, ungültiges Feld usw.) zu übertragen, definieren Sie einen Typ:

Go
type ValidationError struct {
    Field string
    Msg   string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}

Der Aufrufer verwendet errors.As, um einen sicheren Downcast entlang der Kette durchzuführen:

Go
var ve *ValidationError
if errors.As(err, &ve) {
    fmt.Println("invalid field:", ve.Field)
}

errors.As durchläuft die Unwrap()-Kette, bis es einen Fehler findet, der dem übergebenen Zeiger zuordenbar ist.

Umbruch mit %w

fmt.Errorf mit dem Verb %w erzeugt einen Fehler, der das Original umschließt und es für errors.Is/errors.As behält:

Go
if err := db.Query(); err != nil {
    return fmt.Errorf("load users: %w", err)
}

Ohne %w (mit %v oder %s) erhalten Sie einen umfangreicheren String, verlieren aber die Kette: Der Aufrufer kann bestimmte Fehler nicht mehr erkennen.

Regeln für %w:

  • Nur einer pro fmt.Errorf-Aufruf (Multi-Wrap erfordert errors.Join).
  • Das Argument MUSS ein error sein, kein String.
  • Fügen Sie Kontext hinzu, duplizieren Sie nicht die ursprüngliche Nachricht.
Go
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"

// good: useful context
return fmt.Errorf("load config %s: %w", path, err)

errors.Join für mehrere Fehler

Seit Go 1.20 kombiniert errors.Join(errs...) mehrere Fehler zu einem, sodass sie alle von errors.Is/As erkennbar bleiben:

Go
var errs []error
for _, item := range items {
    if err := process(item); err != nil {
        errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
    }
}
if len(errs) > 0 {
    return errors.Join(errs...)
}

Wann sollte panic verwendet werden?

Niemals wegen erwarteter Fehler. Nur in drei Fällen:

  1. Nicht behebbarer Fehler: Eine Invariante wurde an einer Stelle verletzt, die „nicht passieren kann“ (panic("unreachable")).
  2. Initialisierung fehlgeschlagen in init()/main() eines eigenständigen Programms (kritische Konfiguration fehlt → schlägt schnell fehl).
  3. Bibliotheks-API, die es explizit dokumentiert: z. B. regexp.MustCompile (gerät in Panik, wenn das Muster ungültig ist – akzeptabel, da das Muster eine Build-Zeit-Konstante ist).

Alles andere ist error. Verwenden Sie panic nicht als Verknüpfung, um sich nach oben auszubreiten: Es unterbricht die Komposition, überspringt defer Close() und überfordert den Aufrufer.

Übungen

Übung#go.m10.l2.e1
Versuche: 0Wird geladen…

Schließen Sie den Fehler von os.ReadFile mit fmt.Errorf ab, verwenden Sie das Wort %w und fügen Sie den Wettbewerb „config:“ hinzu (mit dem Pfad).

Editor wird geladen…

Lösung nach 3 Versuchen verfügbar

Übung#go.m10.l2.e2
Versuche: 0Wird geladen…

Usa-Fehler.Wird überprüft, ob in der nächsten Zeile ein Fehler aufgetreten ist, der mit dem ErrNotFound-Hinweis korrespondiert und mit „manca!“ versehen ist. Im Großen und Ganzen.

Editor wird geladen…

Lösung nach 3 Versuchen verfügbar

Quiz#go.m10.l2.e3
Bereit

Was ist die idiomatische Verwendung von Panik in einer Bibliothek?

Go
func F(x int) {
  // ??? panic(...)
}
Antwortoptionen