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
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}Vier principes:
erroris de laatste retourzending.- Check onmiddellijk met
if err != nil. - Voeg context toe voordat u deze doorgeeft (met
fmt.Errorf+%w). - Negeer nooit** een fout: handel deze af, registreer deze of stuur deze terug.
_ = 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:
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:
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:
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:
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:
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.Errorfoproep (multi-wrap vereisterrors.Join). - Het argument MOET een
errorzijn, geen string. - Voeg context toe, dupliceer het originele bericht niet.
// 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:
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:
- Onherstelbare bug: een invariant die is geschonden op een plek die "niet kan gebeuren" (
panic("unreachable")). - Mislukte initialisatie in
init()/main()van een zelfstandig programma (kritieke configuratie ontbreekt → mislukt snel). - 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
Wrappa de fout van os.ReadFile con fmt.Fout bij gebruik van het werkwoord %we agggiungendo il contesto 'config:' (con il path).
Oplossing beschikbaar na 3 pogingen
Fouten in de VS kunnen worden geverifieerd als de wrap corrisponde alla sentinella ErrNotFound en stempel 'manca!' in dit geval.
Oplossing beschikbaar na 3 pogingen
Wat is idiomatisch gebruik van paniek in een bibliotheek Go?
func F(x int) {
// ??? panic(...)
}