Leçons du module (2/5)
Gestion idiomatique des erreurs
La gestion des erreurs dans Go est explicite : pas d'exceptions, pas de try/catch. Chaque fonction qui peut échouer renvoie error comme dernière valeur, et l'appelant décide. Ce chapitre rassemble des modèles idiomatiques pour produire, propager et inspecter de manière robuste les erreurs.
La règle d'or
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}Quatre principes :
errorest le dernier retour.- Vérifiez immédiatement avec
if err != nil. - Ajoutez du contexte avant de propager (avec
fmt.Errorf+%w). - N'ignorez jamais une erreur : soit vous la gérez, soit vous la consignez, soit vous la renvoyez.
_ = file.Close() // ESPLICITAMENTE ignorato (sai cosa stai facendo)Erreurs Sentinelle
Une sentinelle d'erreur est une variable globale exportée var ErrXxx = errors.New("..."), que l'appelant peut comparer :
package store
var ErrNotFound = errors.New("not found")
func Get(key string) (string, error) {
if !exists(key) {
return "", ErrNotFound
}
// ...
}L'appelant utilise errors.Is (PAS ==), qui passe par la chaîne d'encapsulage :
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
// gestisci 404
}Exemples standards : io.EOF, sql.ErrNoRows, os.ErrNotExist.
Erreurs personnalisées avec le type
Pour transporter des données structurées (code HTTP, champ invalide, etc.), définissez un type :
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}L'appelant utilise errors.As pour descendre la chaîne en toute sécurité :
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("campo invalido:", ve.Field)
}errors.As parcourt la chaîne de Unwrap() jusqu'à ce qu'il trouve une erreur attribuable au pointeur transmis.
Emballage avec %w
fmt.Errorf avec le verbe %w produit une erreur qui enveloppe l'original, en le préservant pour errors.Is/errors.As :
if err := db.Query(); err != nil {
return fmt.Errorf("load users: %w", err)
}Sans %w (en utilisant %v ou %s) vous obtenez une chaîne plus riche mais perdez la chaîne : l'appelant ne pourra plus reconnaître des erreurs spécifiques.
Règles sur %w :
- Un seul par appel
fmt.Errorf(le multi-wrap nécessiteerrors.Join). - L'argument DOIT être un
error, pas une chaîne. - Ajoutez du contexte, ne dupliquez pas le message d'origine.
// male: duplica
return fmt.Errorf("error: %w", err) // "error: file not found"
// bene: contesto utile
return fmt.Errorf("load config %s: %w", path, err)errors.Join pour plusieurs erreurs
Depuis Go 1.20, errors.Join(errs...) combine plusieurs erreurs en une seule qui les maintient toutes détectables par 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...)
}Quand utiliser panic
Jamais à cause d'erreurs prévues. Seulement dans trois cas :
- Bogue irrécupérable : Invariant violé à un endroit qui "ne peut pas arriver" (
panic("unreachable")). - L'initialisation a échoué dans
init()/main()d'un programme autonome (configuration critique manquante → crash rapide). - API de bibliothèque qui documente explicitement : par ex.
regexp.MustCompile(panique si le modèle n'est pas valide — acceptable car le modèle est constant au moment de la construction).
Tout le reste est error. N'utilisez pas panic comme raccourci pour vous propager vers le haut : cela casse le cadran, ignore le defer Close() et submerge l'appelant.
Exercices
Enveloppez l'erreur os.ReadFile avec fmt.Errorf en utilisant le verbe %w et en ajoutant le contexte 'config:' (avec le chemin).
Solution disponible après 3 tentatives
Utilisez error.Is pour vérifier si l'erreur dans la chaîne d'encapsulage correspond à la sentinelle ErrNotFound et imprimer « manquant ! » dans ce cas.
Solution disponible après 3 tentatives
Quand est-il idiomatique d’utiliser la panique dans une bibliothèque Go ?
func F(x int) {
// ??? panic(...)
}