Nous effectuons un audit des erreurs

Un contrat fréquemment utilisé pour les fonctions qui renvoient une valeur d'erreur d'un type d'interface est que l'appelant ne doit rien savoir à l'avance sur l'état des autres valeurs renvoyées par cet appel sans d'abord vérifier l'erreur.


Dans la plupart des cas, les valeurs d'erreur renvoyées par les fonctions doivent être opaques pour l'appelant. Autrement dit, un test dans lequel l'erreur est nulle indique si l'appel a réussi ou échoué, et c'est tout ce que vous devez faire.



photo d'ici


Un petit nombre de cas, généralement associés à des interactions avec le monde extérieur, comme l'activité du réseau, nécessitent que l'appelant examine la nature de l'erreur afin de décider de répéter l'opération.


Une exigence courante pour les auteurs de packages est de renvoyer les erreurs de type public connues afin que l'appelant puisse utiliser l'assertion de type et les examiner en détail. Je pense que cette pratique conduit à un certain nombre de résultats indésirables:


  • Les types d'erreurs ouverts augmentent la "zone de contact" avec l'API du package.
  • Les nouvelles implémentations ne doivent renvoyer que les types spécifiés dans la déclaration d'interface, même s'ils ne correspondent pas bien.
  • Le type d'erreur, après avoir été ajouté au code, ne peut pas être modifié ou déconseillé sans rompre la compatibilité, ce qui fragilise l'API.

Confirmer le comportement attendu, pas le type d'erreur


Ne prétendez pas que la valeur d'erreur est un type spécifique, mais affirmez plutôt que la valeur implémente un comportement spécifique.


Cette phrase est cohérente avec la nature des interfaces implicites Go, et n'est pas un [sous-type] de la nature des langages basés sur l'héritage. Considérez cet exemple:


func isTimeout(err error) bool { type timeout interface { Timeout() bool } te, ok := err.(timeout) return ok && te.Timeout() } 

L'appelant peut utiliser isTimeout () pour déterminer si l'erreur a expiré en implémentant l'interface d'expiration, puis confirmer si l'erreur a expiré - le tout sans rien savoir du type ou de la source valeurs d'erreur.


Cette méthode permet d'habiller les erreurs, en règle générale, avec des bibliothèques qui ajoutent des explications au chemin d'erreur; à condition que les types d'erreur encapsulés implémentent également les interfaces d'erreur qu'ils encapsulent.


Cela peut sembler un problème insoluble, mais dans la pratique, il existe plusieurs méthodes d'interface généralement acceptées, donc Timeout () bool et Temporary () bool couvriront un large éventail de cas d'utilisation.


En conclusion


Confirmer le comportement attendu, pas le type d'erreur


Pour les auteurs de packages


Si votre package génère des erreurs temporaires, assurez-vous de renvoyer les types d'erreur qui implémentent les méthodes d'interface appropriées. Si vous encapsulez des valeurs d'erreur de sortie, assurez-vous que vos encapsuleurs prennent en charge les interfaces avec lesquelles la valeur d'erreur d'origine est implémentée.


Pour les utilisateurs de packages


Si vous devez rechercher une erreur, utilisez les interfaces pour confirmer le comportement attendu, pas le type d'erreur. Ne demandez pas aux auteurs de packages des types d'erreur ouverts; leur demander d'aligner leurs types avec des interfaces communes en spécifiant les méthodes Timeout () ou Temporary (), selon le cas.


À propos de l'auteur


L'auteur de cet article, Dave Cheney , est l'auteur de nombreux packages populaires pour Go, par exemple, https://github.com/pkg/errors et https://github.com/davecheney/httpstat .


Du traducteur


Bien que l'article original soit daté de fin 2014, il me semble qu'il n'a pas perdu de sa pertinence. L'approche décrite se trouve dans quelques packages, tandis que les autres utilisent l'approche habituelle pour les erreurs.


Les modifications du texte pour augmenter la clarté du matériel sont les bienvenues!

Source: https://habr.com/ru/post/fr440818/


All Articles