Error Wrapping (fmt.Errorf)
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 54 sur 107.
Lorsque des erreurs traversent plusieurs couches de votre application, savoir où une erreur a pris naissance devient crucial. La fonction fmt.Errorf de Go avec le verbe %w vous permet d'envelopper des erreurs, préservant l'erreur originale tout en ajoutant du contexte.
L'emballage d'erreurs crée une chaîne d'erreurs. Chaque couche peut ajouter des informations sur ce qu'elle essayait de faire lorsque l'erreur s'est produite :
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// traiter les données...
return nil
}Le verbe %w est spécial — il enveloppe l'erreur d'origine à l'intérieur de la nouvelle. Ceci est différent de %v, qui convertit simplement l'erreur en une chaîne de caractères et perd l'identité de l'erreur d'origine.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Lorsque LoadSettings échoue, le message d'erreur affiche la chaîne complète : "loading settings: reading config file: open settings.json: no such file or directory". Chaque couche ajoute du contexte, ce qui rend le débogage beaucoup plus facile.
L'erreur enveloppée contient toujours l'erreur d'origine à l'intérieur. Dans la prochaine leçon, vous apprendrez comment désenvelopper ces erreurs et vérifier ce qu'elles contiennent en utilisant errors.Is() et errors.As().
Défi
FacileConstruisons un système de traitement des commandes qui démontre l'enveloppement d'erreurs (error wrapping) à travers plusieurs couches. Vous allez créer une chaîne de fonctions où chaque couche ajoute du contexte aux erreurs, ce qui permet de retracer facilement l'origine exacte des problèmes.
Vous organiserez votre code sur deux fichiers :
orders.go: Créez la logique de traitement des commandes avec plusieurs couches qui enveloppent les erreurs au fur et à mesure qu'elles remontent.Implémentez trois fonctions qui forment une chaîne de traitement :
ValidateItem(itemID string) error- Retourne une erreur avec le message"item not found"si le itemID est"INVALID", sinon retournenilProcessOrder(orderID, itemID string) error- AppelleValidateItem. S'il retourne une erreur, enveloppez-la avec le contexte :"processing order [orderID]: %w". Sinon, retournenilSubmitOrder(customerName, orderID, itemID string) error- AppelleProcessOrder. S'il retourne une erreur, enveloppez-la avec le contexte :"submitting order for [customerName]: %w". Sinon, retournenil
Chaque couche doit utiliser
fmt.Errorfavec le verbe%wpour envelopper l'erreur de la couche inférieure, construisant ainsi une chaîne de contexte.main.go: Lit les détails de la commande depuis l'entrée, appelleSubmitOrder, et affiche le résultat. Si une erreur survient, affichez la chaîne d'erreur complète. En cas de succès, affichez un message de confirmation.
Les entrées suivantes seront fournies :
- Ligne 1 : Nom du client
- Ligne 2 : ID de la commande
- Ligne 3 : ID de l'article
Affichez le résultat :
- Si une erreur survient :
Error: [full error chain] - En cas de succès :
Order [orderID] submitted successfully for [customerName]
Par exemple, avec Alice, ORD-123, et INVALID, votre sortie devrait être :
Error: submitting order for Alice: processing order ORD-123: item not foundRemarquez comment le message d'erreur affiche la chaîne complète — vous pouvez tracer le problème depuis la tentative de soumission au plus haut niveau, à travers le traitement de la commande, jusqu'à l'échec de validation réel. Chaque couche a ajouté son propre contexte en utilisant %w.
Et avec Bob, ORD-456, et ITEM-001, votre sortie devrait être :
Order ORD-456 submitted successfully for BobAide-mémoire
Utilisez fmt.Errorf avec le verbe %w pour envelopper les erreurs tout en préservant l'erreur d'origine :
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
return nil
}Le verbe %w enveloppe l'erreur d'origine à l'intérieur de la nouvelle, contrairement à %v qui convertit simplement l'erreur en une chaîne de caractères et perd l'identité de l'erreur d'origine.
L'enveloppement d'erreurs crée une chaîne où chaque couche ajoute du contexte :
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Le message d'erreur résultant affiche la chaîne complète : "loading settings: reading config file: open settings.json: no such file or directory"
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Lire le nom du client
scanner.Scan()
customerName := scanner.Text()
// Lire l'ID de la commande
scanner.Scan()
orderID := scanner.Text()
// Lire l'ID de l'article
scanner.Scan()
itemID := scanner.Text()
// TODO : Appeler SubmitOrder avec les valeurs d'entrée
// TODO : Si une erreur survient, afficher : Error: [chaîne d'erreur complète]
// TODO : En cas de succès, afficher : Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool