Salt la conținutul principal
Modulul 10 · Lecția 2 din 547/50 în curs~14 min
Lecții din modul (2/5)

Tratarea erorilor idiomatice

Gestionarea erorilor în Go este explicită: fără excepții, fără încercare/captură. Fiecare funcție care poate eșua returnează error ca ultima valoare, iar apelantul decide. Acest capitol colectează modelele idiomatice pentru a produce, propaga și inspecta erorile într-un mod robust.

Regula de aur

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

Patru principii:

  1. error este ultima retur.
  2. Verificați imediat cu if err != nil.
  3. Adăugați context înainte de propagare (cu fmt.Errorf + %w).
  4. Nu ignora niciodată o eroare: fie tratează-o, înregistrează-o, fie returnează-o.
Go
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)

Erori santinelă

O eroare sentinel este o variabilă globală exportată var ErrXxx = errors.New("...") cu care apelantul o poate compara:

Go
package store

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

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

Apelantul folosește errors.Is (NU ==), care parcurge lanțul de împachetare:

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

Exemple standard: io.EOF, sql.ErrNoRows, os.ErrNotExist.

Erori de tastare personalizate

Pentru a transporta date structurate (cod HTTP, câmp nevalid etc.), definiți un tip:

Go
type ValidationError struct {
    Field string
    Msg   string
}

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

Apelantul folosește errors.As pentru a coborî în siguranță de-a lungul lanțului:

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

errors.As parcurge lanțul Unwrap() până găsește o eroare care poate fi atribuită indicatorului transmis.

Încheiere cu %w

fmt.Errorf cu verbul %w produce o eroare care încheie cea originală, păstrându-l pentru errors.Is/errors.As:

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

Fără %w (folosind %v sau %s) obțineți un șir mai bogat, dar pierdeți lanțul: apelantul nu mai poate recunoaște anumite erori.

Reguli pentru %w:

  • Doar unul pe apel fmt.Errorf (încheierea multiplă necesită errors.Join).
  • Argumentul TREBUIE să fie un error, nu un șir.
  • Adăugați context, nu duplicați mesajul original.
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 pentru mai multe erori

De la Go 1.20, errors.Join(errs...) combină mai multe erori într-o singură care le face pe toate detectabile prin 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...)
}

Când să utilizați panic

Niciodată pentru erorile așteptate. Numai în trei cazuri:

  1. Eroare irecuperabilă: un invariant încălcat într-un loc care „nu se poate întâmpla” (panic("unreachable")).
  2. Inițializarea eșuată în init()/main() a unui program independent (lipsește configurația critică → eșuează rapid).
  3. Bibliotecă API care o documentează în mod explicit: de exemplu, regexp.MustCompile (intră în panică dacă modelul este invalid - acceptabil deoarece modelul este o constantă de timp de construcție).

Toate celelalte sunt error. Nu folosiți panic ca o comandă rapidă pentru a propaga: întrerupe compoziția, omite defer Close() și copleșește apelantul.

Exerciții

Exercițiu#go.m10.l2.e1
Încercări: 0Se încarcă…

Wrappa l'errore di os.ReadFile cu fmt.Errorf folosind cuvântul %w și adăugând contextul „config:” (cu calea).

Se încarcă editorul...

Soluție disponibilă după 3 încercări

Exercițiu#go.m10.l2.e2
Încercări: 0Se încarcă…

Usa errors.Is per verificare se err nella catena di wrap corrisponde alla sentinella ErrNotFound e stampa 'manca!' in tal caz.

Se încarcă editorul...

Soluție disponibilă după 3 încercări

Quiz#go.m10.l2.e3
Gata

Când este idiomatic să folosiți panica într-o librerie Go?

Go
func F(x int) {
  // ??? panic(...)
}
Opțiuni de răspuns