errors.Is() et errors.As()
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 56 sur 107.
Lorsque les erreurs sont enveloppées, les simples vérifications d'égalité comme err == ErrNotFound ne fonctionnent plus. Le paquet errors fournit deux fonctions pour inspecter les chaînes d'erreurs enveloppées : errors.Is() et errors.As().
errors.Is() vérifie si une erreur dans la chaîne correspond à une erreur sentinelle spécifique :
import "errors"
var ErrNotFound = errors.New("not found")
func FindUser(id int) error {
return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}
err := FindUser(42)
if errors.Is(err, ErrNotFound) {
fmt.Println("User doesn't exist")
}Même si l'erreur retournée est enveloppée, errors.Is() déballe la chaîne et trouve ErrNotFound à l'intérieur.
errors.As() vérifie si une erreur dans la chaîne correspond à un type spécifique et l'extrait :
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("Invalid field:", ve.Field)
}Utilisez errors.Is() lors de la vérification d'erreurs sentinelles, et errors.As() lorsque vous avez besoin d'accéder aux champs d'un type d'erreur personnalisé. Les deux fonctions parcourent l'ensemble de la chaîne d'erreurs, ce qui les rend essentielles pour gérer correctement les erreurs enveloppées.
Défi
FacileConstruisons un système de traitement de fichiers qui démontre comment inspecter correctement les chaînes d'erreurs encapsulées à l'aide de errors.Is() et errors.As(). Vous allez créer des erreurs qui sont encapsulées (wrapped) au fur et à mesure qu'elles passent par différentes couches, puis utiliser ces fonctions pour les identifier et en extraire des informations.
Vous organiserez votre code sur deux fichiers :
errors.go: Définissez l'infrastructure d'erreur pour votre système de fichiers.Créez une erreur sentinelle :
ErrPermissionDeniedavec le message"permission denied"
Créez un type d'erreur personnalisé :
- Une structure
FileErroravec les champsFilename(string) etOperation(string) - Sa méthode
Error()doit retourner :[Operation] failed on file: [Filename]
Implémentez deux fonctions qui simulent des opérations de fichiers et encapsulent les erreurs :
ReadFile(filename string) error- Si le nom de fichier est"secret.txt", retournezErrPermissionDenied. Si le nom de fichier est"missing.txt", retournez un*FileErroravec l'opération"read"et le nom de fichier donné. Sinon, retourneznil.ProcessFile(filename string) error- AppelleReadFile. Si une erreur est retournée, encapsulez-la en utilisantfmt.Errorfavec le format"processing failed: %w". Sinon, retourneznil.
main.go: Lisez un nom de fichier depuis l'entrée et appelezProcessFile. Utilisezerrors.Is()eterrors.As()pour inspecter la chaîne d'erreurs encapsulée et afficher les messages appropriés en fonction de ce que vous trouvez à l'intérieur.
L'entrée suivante sera fournie :
- Ligne 1 : Nom du fichier à traiter
Gérez le résultat en inspectant la chaîne d'erreurs :
- Si
errors.Is()trouveErrPermissionDenieddans la chaîne : affichez le message d'erreur complet, puis sur une nouvelle ligne affichezAccess denied - check file permissions - Si
errors.As()trouve un*FileErrordans la chaîne : affichez le message d'erreur complet, puis sur une nouvelle ligne affichezFile issue: [Filename] during [Operation] - Si aucune erreur ne survient : affichez
File '[filename]' processed successfully
Par exemple, pour secret.txt, votre sortie devrait être :
processing failed: permission denied
Access denied - check file permissionsEt pour missing.txt, votre sortie devrait être :
processing failed: read failed on file: missing.txt
File issue: missing.txt during readEt pour data.txt, votre sortie devrait être :
File 'data.txt' processed successfullyRemarquez comment, même si les erreurs sont encapsulées par ProcessFile, vous pouvez toujours détecter l'erreur sentinelle d'origine avec errors.Is() et extraire le type d'erreur personnalisé avec errors.As(). Ces fonctions parcourent toute la chaîne d'erreurs pour trouver ce que vous cherchez.
Aide-mémoire
Lorsque les erreurs sont enveloppées (wrapped), utilisez errors.Is() et errors.As() du package errors pour inspecter la chaîne d'erreurs.
errors.Is() vérifie si une erreur dans la chaîne correspond à une erreur sentinelle :
import "errors"
var ErrNotFound = errors.New("not found")
err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
// ErrNotFound a été trouvé dans la chaîne
}errors.As() vérifie si une erreur dans la chaîne correspond à un type spécifique et l'extrait :
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
// ve contient maintenant le ValidationError provenant de la chaîne
fmt.Println(ve.Field)
}Utilisez errors.Is() pour les erreurs sentinelles et errors.As() lorsque vous avez besoin d'accéder aux champs de types d'erreurs personnalisés. Les deux fonctions parcourent l'intégralité de la chaîne d'erreurs.
Essayez vous-même
package main
import (
"errors"
"fmt"
)
func main() {
// Lire le nom du fichier depuis l'entrée
var filename string
fmt.Scanln(&filename)
// Appeler ProcessFile avec le nom du fichier
err := ProcessFile(filename)
// TODO: Gérer le résultat en inspectant la chaîne d'erreurs
//
// Utiliser errors.Is() pour vérifier si ErrPermissionDenied est dans la chaîne :
// - Si trouvé : afficher l'erreur complète, puis "Access denied - check file permissions"
//
// Utiliser errors.As() pour vérifier si un *FileError est dans la chaîne :
// - Si trouvé : afficher l'erreur complète, puis "File issue: [Filename] during [Operation]"
//
// Si aucune erreur : afficher "File '[filename]' processed successfully"
_ = errors.Is // indice : utilisez errors.Is()
_ = errors.As // indice : utilisez errors.As()
_ = err
}
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