ProgrammationDéveloppeur Backend

Parlez-nous du travail avec les erreurs via le package error wrapping en Go : qu'est-ce que le wrapping d'erreurs, pourquoi est-ce important et comment implémenter correctement l'encapsulation des erreurs ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Contexte du problème :

Avant Go 1.13, l'erreur était une simple interface. Pour un contexte supplémentaire, il était courant de créer des types d'erreur spécifiques, mais il n'y avait pas de mécanisme structuré d'encapsulation, comme dans la Java ou le C# modernes. Avec l'arrivée de Go 1.13, un moyen standard d'encapsulation (wrapping) des erreurs a été introduit via les fonctions fmt.Errorf et pour identifier la cause profonde (errors.Is/errors.As).

Problème :

Dans des applications complexes, où des erreurs peuvent survenir à différents niveaux de la pile, il est essentiel de ne pas perdre le contexte lors du retour d'erreurs de la profondeur du code. Sinon, le débogage devient difficile et il est impossible de comprendre où dans la chaîne l'échec s'est produit.

Solution :

Go propose d'encapsuler les erreurs dans de nouveaux objets d'erreur qui contiennent la cause. Pour cela, %w est utilisé dans fmt.Errorf, et pour vérifier les causes profondes — errors.Is ou errors.As du package errors.

Exemple de code :

import ( "errors" "fmt" ) var ErrNotFound = errors.New("not found") func getData() error { return fmt.Errorf("service database: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("Cause trouvée : non trouvé") } else { fmt.Println("Autre erreur :", err) } }

Caractéristiques clés :

  • Utilisation de %w pour l'encapsulation d'erreurs via fmt.Errorf.
  • Identification des causes imbriquées des erreurs via errors.Is et errors.As.
  • Compatibilité avec les types d'erreurs personnalisés lors de l'encapsulation et du déballage.

Questions pièges.

Quel opérateur de formatage est nécessaire pour encapsuler des erreurs via fmt.Errorf ?

Il faut utiliser %w, et non %v — seul %w permet de prendre en charge le déballage.

Exemple de code :

fmt.Errorf("erreur : %w", err)

Peut-on créer manuellement des chaînes d'erreurs sans fmt.Errorf et tout de même les identifier via errors.Is ?

Non, il faut implémenter l'interface Unwrap, sinon les fonctions standard ne déballeront pas la chaîne.

Exemple de code de l'interface :

type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }

Les erreurs retournent-elles des valeurs nil après un wrapping, si l'erreur imbriquée était nil ?

Non, si l'erreur imbriquée est nil, alors l'erreur encapsulée est également nil seulement si elle est correctement utilisée. Mais si l'on crée manuellement une structure wrapper où le champ d'erreur est nil, elle ne sera pas nil. Cela conduit souvent à de la confusion lors de la vérification.

Erreurs typiques et anti-patterns

  • Ne pas utiliser %w, mais seulement %v — cela entraîne la perte de la chaîne d'analyse.
  • Ne pas implémenter Unwrap() pour vos structures d'erreur.
  • Ne pas faire de vérification via errors.Is/As, mais simplement comparer directement l'erreur.
  • Créer de nouvelles erreurs sans raison (perte de contexte).

Exemple de la vie réelle

Cas négatif

Dans l'application, une nouvelle erreur était simplement retournée avec un message à chaque niveau :

return errors.New("erreur d'écriture dans la BDD")

Avantages :

  • Simple et rapide.

Inconvénients :

  • Impossible de distinguer que l'erreur est en fait « not found » de la profondeur, ce qui peut casser la logique métier en haut.
  • Perte d'informations sur la pile et la source de l'erreur.

Cas positif

Utilisation de l'encapsulation des erreurs avec %w et analyse via errors.Is :

if errors.Is(err, ErrNotFound) { return fmt.Errorf("erreur de niveau service : %w", err) }

Avantages :

  • La cause peut être correctement identifiée à n'importe quel niveau.
  • Le débogage est plus facile, le contexte original est toujours visible.

Inconvénients :

  • Nécessite de comprendre comment fonctionne le wrapping et une rédaction qualifiée des erreurs.