Direct naar de hoofdinhoud
Module 10 · Les 2 van 547/50 in de cursus~14 min
Modulelessen (2/5)

Idiomatische foutafhandeling

Foutafhandeling in Go is expliciet: geen uitzonderingen, geen try/catch. Elke functie die kan mislukken, retourneert error als laatste waarde, en de aanroeper beslist. Dit hoofdstuk verzamelt de idiomatische patronen om fouten op een robuuste manier te produceren, te verspreiden en te inspecteren.

De gouden regel

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

Vier principes:

  1. error is de laatste retourzending.
  2. Check onmiddellijk met if err != nil.
  3. Voeg context toe voordat u deze doorgeeft (met fmt.Errorf + %w).
  4. Negeer nooit** een fout: handel deze af, registreer deze of stuur deze terug.
Go
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)

Sentinel-fouten

Een schildwachtfout is een geëxporteerde globale variabele var ErrXxx = errors.New("...") waarmee de beller kan vergelijken:

Go
package store

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

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

De beller gebruikt errors.Is (NIET ==), waarmee de wrap-keten wordt doorlopen:

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

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

Aangepaste typefouten

Om gestructureerde gegevens (HTTP-code, ongeldig veld, enz.) over te dragen, definieert u een type:

Go
type ValidationError struct {
    Field string
    Msg   string
}

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

De beller gebruikt errors.As om veilig langs de keten te downcasten:

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

errors.As loopt door de Unwrap() keten totdat er een fout wordt gevonden die kan worden toegeschreven aan de doorgegeven aanwijzer.

Inpakken met %w

fmt.Errorf met het werkwoord %w levert een fout op die de oorspronkelijke fout verpakt en deze bewaart voor errors.Is/errors.As:

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

Zonder %w (met %v of %s) krijgt u een rijkere string, maar verlies de keten: de beller kan specifieke fouten niet meer herkennen.

Regels voor %w:

  • Slechts één per fmt.Errorf oproep (multi-wrap vereist errors.Join).
  • Het argument MOET een error zijn, geen string.
  • Voeg context toe, dupliceer het originele bericht niet.
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 voor meerdere fouten

Sinds Go 1.20 combineert errors.Join(errs...) meerdere fouten in één, waardoor ze allemaal detecteerbaar blijven door errors.Is/As:

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

Wanneer moet u panic gebruiken

Nooit voor verwachte fouten. Slechts in drie gevallen:

  1. Onherstelbare bug: een invariant die is geschonden op een plek die "niet kan gebeuren" (panic("unreachable")).
  2. Mislukte initialisatie in init()/main() van een zelfstandig programma (kritieke configuratie ontbreekt → mislukt snel).
  3. Bibliotheek-API die dit expliciet documenteert: bijvoorbeeld regexp.MustCompile (raakt in paniek als het patroon ongeldig is - acceptabel omdat het patroon een constante in de bouwtijd is).

Al het andere is error. Gebruik panic niet als sneltoets om omhoog te verspreiden: het breekt de compositie, slaat defer Close() over en overweldigt de beller.

Oefeningen

Oefening#go.m10.l2.e1
Pogingen: 0Laden…

Wrappa de fout van os.ReadFile con fmt.Fout bij gebruik van het werkwoord %we agggiungendo il contesto 'config:' (con il path).

Editor laden…

Oplossing beschikbaar na 3 pogingen

Oefening#go.m10.l2.e2
Pogingen: 0Laden…

Fouten in de VS kunnen worden geverifieerd als de wrap corrisponde alla sentinella ErrNotFound en stempel 'manca!' in dit geval.

Editor laden…

Oplossing beschikbaar na 3 pogingen

Quiz#go.m10.l2.e3
Klaar

Wat is idiomatisch gebruik van paniek in een bibliotheek Go?

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