Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 :

    • ErrPermissionDenied avec le message "permission denied"

    Créez un type d'erreur personnalisé :

    • Une structure FileError avec les champs Filename (string) et Operation (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", retournez ErrPermissionDenied. Si le nom de fichier est "missing.txt", retournez un *FileError avec l'opération "read" et le nom de fichier donné. Sinon, retournez nil.
    • ProcessFile(filename string) error - Appelle ReadFile. Si une erreur est retournée, encapsulez-la en utilisant fmt.Errorf avec le format "processing failed: %w". Sinon, retournez nil.
  • main.go : Lisez un nom de fichier depuis l'entrée et appelez ProcessFile. Utilisez errors.Is() et errors.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() trouve ErrPermissionDenied dans la chaîne : affichez le message d'erreur complet, puis sur une nouvelle ligne affichez Access denied - check file permissions
  • Si errors.As() trouve un *FileError dans la chaîne : affichez le message d'erreur complet, puis sur une nouvelle ligne affichez File 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 permissions

Et pour missing.txt, votre sortie devrait être :

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

Et pour data.txt, votre sortie devrait être :

File 'data.txt' processed successfully

Remarquez 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
}
quiz iconTestez-vous

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