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
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}Patru principii:
erroreste ultima retur.- Verificați imediat cu
if err != nil. - Adăugați context înainte de propagare (cu
fmt.Errorf+%w). - Nu ignora niciodată o eroare: fie tratează-o, înregistrează-o, fie returnează-o.
_ = 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:
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:
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:
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:
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:
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.
// 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:
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:
- Eroare irecuperabilă: un invariant încălcat într-un loc care „nu se poate întâmpla” (
panic("unreachable")). - Inițializarea eșuată în
init()/main()a unui program independent (lipsește configurația critică → eșuează rapid). - 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
Wrappa l'errore di os.ReadFile cu fmt.Errorf folosind cuvântul %w și adăugând contextul „config:” (cu calea).
Soluție disponibilă după 3 încercări
Usa errors.Is per verificare se err nella catena di wrap corrisponde alla sentinella ErrNotFound e stampa 'manca!' in tal caz.
Soluție disponibilă după 3 încercări
Când este idiomatic să folosiți panica într-o librerie Go?
func F(x int) {
// ??? panic(...)
}