0Pricing
Go Academy · Lektion

Fehler-Wrapping und -Unwrapping

%w, errors.Is und errors.As verwenden

Fehler-Wrapping und -Unwrapping ist eine kostenlose Go Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Go Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Go Academy-Kurs umfasst insgesamt 4 Lektionen.

Was ist Fehler-Wrapping?

Fehler-Wrapping bettet einen ursprünglichen Fehler in einen neuen Fehler ein. Dadurch bleibt das Original zur programmgesteuerten Untersuchung erhalten, während zusätzlicher Kontext hinzugefügt wird. In Go wurde dies mit Go 1.13 offiziell eingeführt:

package main
import ("fmt"; "errors")

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

Das Verb %w

Verwenden Sie %w (nicht %v) in fmt.Errorf, um einen Fehler zu wrappen. Auf den gewrappten Fehler kann über errors.Unwrap zugegriffen werden:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

errors.Unwrap gibt den nächsten Fehler in der Kette (den gewrappten Fehler) oder nil zurück, wenn keiner vorhanden ist:

package main
import ("fmt"; "errors")

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is — Kette durchlaufen

errors.Is(err, target) entpackt die Fehlerkette rekursiv, bis eine Übereinstimmung gefunden wird oder nil erreicht ist:

package main
import ("fmt"; "errors")

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

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As — Kette nach Typen durchsuchen

errors.As(err, &target) durchläuft die Kette und sucht nach einem Fehler, der dem Typ von target zugewiesen werden kann:

package main
import ("fmt"; "errors")

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

Eigenes Unwrap für Fehlerketten

Implementieren Sie Unwrap() error für Ihren eigenen Fehlertyp, damit errors.Is und errors.As unterstützt werden:

package main
import ("fmt"; "errors")

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

%v vs. %w in fmt.Errorf

Der Unterschied zwischen %v und %w:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

Wrapping in der Praxis: HTTP-Handler

Konsistentes Fehler-Wrapping erleichtert das Debugging in Produktionssystemen:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join — Mehrfaches Wrapping (Go 1.20+)

errors.Join erstellt einen Fehler, der mehrere Fehler wrapped. errors.Is prüft sie alle:

package main
import ("fmt"; "errors")

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

Best Practices zum Entpacken

Richtlinien für Fehler-Wrapping:

  • Wrappen Sie mit %w, damit der Fehler zur programmgesteuerten Untersuchung erhalten bleibt
  • Fügen Sie zur Kontextangabe den Operationsnamen hinzu: fmt.Errorf("funcName: %w", err)
  • Wrappen Sie Sentinel-Fehler nicht, wenn Aufrufer errors.Is nicht verwenden
  • Vermeiden Sie es, denselben Kontext auf jeder Ebene doppelt zu wrappen
  • Implementieren Sie Unwrap() für eigene Typen, damit sie an Fehlerketten teilnehmen können

Schnelltest

Was ist der Unterschied zwischen der Verwendung von %v und %w in fmt.Errorf?

Zusammenfassung: Fehler-Wrapping und -Entpacken

Zusammenfassung:

  • fmt.Errorf("ctx: %w", err) wrapped einen Fehler mit Kontext
  • errors.Unwrap gibt den nächsten Fehler in der Kette zurück
  • errors.Is durchläuft die Kette auf der Suche nach einer Identitätsübereinstimmung
  • errors.As durchläuft die Kette auf der Suche nach einer Typübereinstimmung
  • Eigene Typen implementieren Unwrap() error
  • errors.Join wrapped mehrere Fehler (Go 1.20+)

Häufig gestellte Fragen

Ist die Lektion „Fehler-Wrapping und -Unwrapping“ kostenlos?

Ja — der vollständige Text von „Fehler-Wrapping und -Unwrapping“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Go Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Go Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Fehler-Wrapping und -Unwrapping“?

%w, errors.Is und errors.As verwenden Du übst Go Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Go Academy zu starten?

Keine Vorkenntnisse erforderlich. Go Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Fehler-Wrapping und -Unwrapping“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Go Academy-Lektion Code schreiben und ausführen?

Ja. Jede Go Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Das error-Interface
  2. Eigene Fehler erstellen
  3. Fehler-Wrapping und -Unwrapping
  4. panic, recover und defer
← Zurück zu Go Academy