errors.Join în Go

Erori (errors) Go 1.20

Unește mai multe erori într-una singură; mesajele apar pe linii separate, iar errors.Is/As le găsesc pe fiecare.

Sintaxă
func Join(errs ...error) error

Ce face errors.Join

Când validezi un formular sau procesezi mai multe fișiere, vrei să raportezi toate problemele, nu doar prima. errors.Join(e1, e2, e3) creează o eroare care le conține pe toate. Mesajul ei (Error()) este format din mesajele lor, separate prin linie nouă.

Valorile nil sunt ignorate, iar dacă toate sunt nil (sau nu dai niciuna), Join returnează nil. Asta permite modelul err = errors.Join(err, verificareNoua()) într-o buclă. errors.Is și errors.As caută în toate erorile unite.

Parametri

Parametru Descriere
errs Erorile de unit (oricâte, nil-urile sunt ignorate).

Ce returnează

O eroare care le conține pe toate, sau nil dacă nu există nicio eroare diferită de nil.

Exemple

Exemplul 1: Validarea completă a unui formular

package main

import (
	"errors"
	"fmt"
	"strings"
)

var ErrCampGol = errors.New("câmp obligatoriu gol")

func valideaza(nume, email, telefon string) error {
	var err error
	if nume == "" {
		err = errors.Join(err, fmt.Errorf("nume: %w", ErrCampGol))
	}
	if !strings.Contains(email, "@") {
		err = errors.Join(err, fmt.Errorf("email: %q nu este o adresă validă", email))
	}
	if len(telefon) != 10 {
		err = errors.Join(err, fmt.Errorf("telefon: are %d cifre în loc de 10", len(telefon)))
	}
	return err
}

func main() {
	err := valideaza("", "ana.example.ro", "07221234")
	fmt.Println("Probleme găsite:")
	fmt.Println(err)
	fmt.Println("Conține ErrCampGol:", errors.Is(err, ErrCampGol))

	fmt.Println("Formular corect:", valideaza("Ana", "ana@example.ro", "0722123456"))
}

Rezultat

Probleme găsite:
nume: câmp obligatoriu gol
email: "ana.example.ro" nu este o adresă validă
telefon: are 8 cifre în loc de 10
Conține ErrCampGol: true
Formular corect: <nil>

Exemplul 2: Erorile unite, una câte una

Eroarea creată de Join are metoda Unwrap() []error, prin care poți lua fiecare eroare în parte.

package main

import (
	"errors"
	"fmt"
)

func main() {
	err := errors.Join(
		errors.New("fișierul ianuarie.csv lipsește"),
		nil,
		errors.New("fișierul martie.csv are 3 linii invalide"),
	)
	if multe, ok := err.(interface{ Unwrap() []error }); ok {
		for i, e := range multe.Unwrap() {
			fmt.Printf("%d. %v\n", i+1, e)
		}
	}
	fmt.Println("Join fără erori:", errors.Join(nil, nil))
}

Rezultat

1. fișierul ianuarie.csv lipsește
2. fișierul martie.csv are 3 linii invalide
Join fără erori: <nil>

Greșeli frecvente

  • Te aștepți ca errors.Unwrap să returneze erorile unite. errors.Unwrap citește doar Unwrap() error și returnează nil pentru o eroare creată cu Join.
  • Afișezi eroarea unită pe un singur rând într-un jurnal: mesajul are linii noi. Înlocuiește-le sau parcurge erorile pe rând.

Comenzi înrudite

  • errors.Is: Verifică dacă eroarea err este target sau o conține, căutând prin toate erorile împachetate cu %w sau Join.
  • errors.As: Caută în lanțul de erori prima eroare de un anumit tip și o pune în target (un pointer), ca să-i citești câmpurile.
  • errors.Unwrap: Returnează eroarea împachetată direct în err (un singur nivel), sau nil dacă err nu împachetează nimic.
  • errors.New: Creează o eroare simplă cu mesajul dat; folosită și pentru erori-santinelă (var ErrX = errors.New(…)).