Go repose sur une conception des erreurs relativement différente de celle de langages comme Java, C++ et JavaScript, ainsi que Python : dans Go, l’erreur est une valeur du type intégré error, et la fonction la renvoie généralement à l’appelant avec les autres valeurs de retour. La vérification et la gestion des erreurs deviennent ainsi une partie visible du flux du programme, au lieu d’être transférées à un mécanisme d’exceptions distinct.
Le guide de JetBrains, rédigé à l’origine par le contributeur communautaire Christoph Berger puis transféré sur le blog JetBrains Go, présente un ensemble de techniques et de pratiques pour gérer les erreurs d’entrée et de sortie, de réseau, de validation des données, entre autres. Il a été mis à jour en août 2026 afin de suivre les dernières évolutions du langage Go.
Le retour comme point de départ
Lorsqu’une fonction ne dispose pas d’un contexte suffisant pour traiter le problème, elle doit renvoyer l’erreur à la fonction appelante. Go place généralement la valeur d’erreur en dernier dans la liste des valeurs de retour ; une fonction comme ReadFile() renvoie ainsi le contenu du fichier et une erreur dont la valeur est nil en cas de succès, ou une autre valeur en cas d’échec.
Le guide explique que l’appelant doit tester l’erreur immédiatement plutôt que de l’ignorer. Si la fonction a ouvert une ressource, comme un fichier ou une connexion réseau, il convient d’utiliser defer pour la nettoyer à la sortie, mais seulement après avoir vérifié que l’ouverture a réussi, car tenter de fermer une ressource invalide peut provoquer un problème supplémentaire.
Ajouter du contexte sans détériorer la structure de l’erreur
Une erreur peut traverser une chaîne de fonctions avant d’être traitée ou enregistrée. Au cours de ce passage, chaque fonction peut ajouter des informations utiles, comme l’opération qui a échoué ou le chemin qu’elle traitait. Toutefois, transformer l’erreur en texte en concaténant err.Error() fait perdre la chaîne et la structure typée de l’erreur.
La bonne méthode consiste à utiliser fmt.Errorf() avec le verbe %w afin d’enrober l’erreur tout en conservant l’erreur d’origine. Cela permet ensuite d’utiliser errors.Unwrap() pour accéder à une couche, ou errors.Is() et errors.As() pour tester des erreurs précises au sein de la chaîne d’enrobage. Go 1.26 ajoute la fonction générique errors.AsType(), une alternative sûre du point de vue des types à As() ; elle renvoie l’erreur correspondante et une valeur booléenne, et permet au compilateur de détecter les erreurs de type qui pourraient apparaître à l’exécution avec l’ancienne méthode.
Erreurs multiples et contexte annulé
Les erreurs ne forment pas toujours une chaîne linéaire. Lors du traitement d’un ensemble de fichiers, par exemple, certaines opérations peuvent réussir et d’autres échouer. Le paquet standard fournit errors.Join(), disponible depuis Go 1.20, pour regrouper plusieurs erreurs en une seule valeur tout en conservant le contenu traité avec succès.
Le guide signale que errors.Unwrap() renvoie une seule valeur et renvoie donc nil lorsqu’il traite une erreur combinée. Pour accéder aux erreurs regroupées, il faut vérifier que la valeur implémente l’interface qui fournit Unwrap() []error. Depuis Go 1.20, il est également possible d’utiliser context.WithCancelCause() pour associer à l’annulation du contexte une cause personnalisée, puis de récupérer cette cause avec context.Cause(ctx), au lieu de se limiter à la valeur générale context.Canceled.
Quand panic est-il approprié ?
panic et recover ne doivent pas remplacer habituellement la vérification des erreurs. Les erreurs prévisibles, comme les entrées utilisateur invalides, les fichiers manquants ou les délais d’attente réseau, doivent être traitées et renvoyées par le flux de retour normal.
La panique devient appropriée lorsque le problème est inattendu et qu’aucun traitement pertinent n’est possible, comme l’échec de compilation d’une expression régulière constante dont la validité aurait dû être vérifiée à l’avance. Dans les serveurs HTTP, l’application peut utiliser recover() à l’intérieur d’une fonction différée pour empêcher l’effondrement du traitement de la requête en cours d’affecter les autres requêtes, lorsque cela est possible. En revanche, des situations comme l’épuisement de la mémoire peuvent ne laisser à l’application aucune possibilité pratique de poursuivre son exécution.
Quels sont les points importants en pratique pour les développeurs ?
- N’ignorez pas les erreurs : affecter la valeur d’erreur à l’identifiant vide ou abandonner l’unique valeur de retour peut retarder la détection du problème et rendre ses conséquences ultérieures plus difficiles à diagnostiquer.
- Enregistrez au bon endroit : la fonction doit traiter l’erreur ou la renvoyer à l’appelant. Le guide recommande aux bibliothèques d’éviter d’enregistrer elles-mêmes les erreurs, car leurs utilisateurs peuvent préférer des journaux ou des destinations de sortie différents.
- Utilisez des types appropriés : les types d’erreur personnalisés peuvent contenir des informations supplémentaires, comme le fait fs.PathError, qui fournit l’opération, le chemin et l’erreur interne.
- Faites la distinction entre les erreurs réseau : net.OpError permet d’examiner la nature de l’échec de la connexion, et une erreur temporaire peut autoriser une nouvelle tentative ou l’utilisation d’une stratégie telle que le recul exponentiel.
- Exploitez le nombre d’octets dans les opérations d’entrée et de sortie : l’interface io.Reader renvoie le nombre d’octets traités avec l’erreur, ce qui peut aider à reprendre l’opération au lieu de renvoyer toutes les données.
- Faites attention à io.EOF : cette valeur indique la fin réussie d’un flux de lecture selon la sémantique du paquet ; il ne s’agit pas nécessairement d’un échec à enregistrer comme une erreur ordinaire.
- N’utilisez pas log.Fatal() sans discernement : cette fonction appelle os.Exit(), ce qui entraîne le contournement des fonctions différées. Le guide suggère donc de limiter son utilisation, ou d’utiliser os.Exit() dans la fonction main().
La conclusion éditoriale de certi.news est que la clarté du cheminement des erreurs fait partie de la conception du programme, et ne constitue pas simplement un style d’écriture verbeux. Un enrobage structuré, des types appropriés et un enregistrement responsable rendent le diagnostic exploitable, tandis que le recours à panic, aux messages génériques ou à l’abandon des erreurs masque les informations dont le développeur aura besoin par la suite. Le compromis pratique reste lié au contexte de l’application : un serveur de requêtes peut avoir besoin d’une récupération limitée, tandis qu’une erreur irrécupérable peut nécessiter l’arrêt et le redémarrage.