En Go, les interfaces fmt.Stringer et error sont utilisées pour gérer la façon dont une valeur de structure est convertie en chaîne et comment elle implémente une erreur, respectivement. Ces interfaces fournissent des moyens universels de journalisation, d'affichage et de gestion des erreurs, rendant le code plus flexible et compréhensible.
Historique de la question :
Depuis les premières versions de Go, l'interface Stringer est devenue essentielle pour un affichage contrôlé et esthétique des structures. L'interface error s'est révélée fondamentale pour le traitement des erreurs à tous les niveaux du code.
Problème :
Les programmeurs obtiennent souvent des sorties peu informatives ou des messages d'erreur inattendus car ils n'ont pas implémenté ces méthodes, ou l'ont fait de manière non standard. De plus, une mauvaise implémentation peut entraîner des affichages récursifs, des pannes et des erreurs difficiles à lire.
Solution :
Exemple de code :
package main import "fmt" type User struct { Name string ID int } func (u User) String() string { return fmt.Sprintf("User<%d:%s>", u.ID, u.Name) } type MyError struct { Msg string } func (e MyError) Error() string { return "MyError: " + e.Msg } func main() { u := User{Name: "Bob", ID: 10} fmt.Println(u) // appelle String() err := MyError{Msg:"fail"} fmt.Println(err) // appelle Error() }
Caractéristiques clés :
Est-il obligatoire d'implémenter String() ou Error() comme méthodes de valeurs ou peut-on utiliser des pointeurs ?
Les deux options sont valables, mais l'implémentation sur le récepteur pointeur et le récepteur valeur affecte les types d'objets sur lesquels la méthode s'applique. On utilise généralement le récepteur pointeur pour les structures mutables.
func (u *User) String() string {...}
Peut-on utiliser fmt.Sprintf à l'intérieur de String() ou Error() ?
Oui, mais il faut faire attention à ne pas provoquer une récursion infinie (par exemple, afficher une structure ayant le même type à l'intérieur de String()). Il est recommandé d'éviter d'utiliser fmt.Print à l'intérieur de String() si cela appelle à nouveau String().
func (u User) String() string { return fmt.Sprintf("%v", u.Name) } // sécurisé
Que se passe-t-il si la méthode Error() retourne une chaîne vide ?
Les erreurs avec des chaînes vides sont considérées comme des valeurs error valides, mais la journalisation perd son sens. L'interface error ne définit pas le comportement en cas de chaîne vide, mais il est généralement admis de fournir toujours un message informatif.
Un développeur affiche une structure via %+v, sans avoir implémenté String(), obtenant ainsi des dumps de champs inutilisables dans les journaux.
Avantages :
Inconvénients :
Le team lead oblige l'équipe à implémenter String() et Error() pour toutes les structures publiques. En conséquence, la logique métier gère les erreurs de manière centralisée, et l'administrateur et les journaux de débogage deviennent lisibles.
Avantages :
Inconvénients :