ProgrammationDéveloppeur Backend

Comment fonctionne la méthode String() en Go pour les types personnalisés ? Quand doit-on l'implémenter, et comment les spécificités de son implémentation peuvent-elles affecter la sortie des données et les logs ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Si un type personnalisé implémente la méthode

String() string

alors lors de l'utilisation de cette valeur dans les fonctions fmt (par exemple, fmt.Println, fmt.Printf("%v"), le package log), cette méthode est appelée pour obtenir une représentation sous forme de chaîne.

Il est conseillé d'implémenter String() pour les types d'entités personnalisées afin d'obtenir une sortie lisible et significative au lieu du standard de fmt (%v). Il est recommandé de rendre la sortie concise, adéquate au contexte d'utilisation, et, si possible, sécurisée (sans fuites de données sensibles).

Exemple d'implémentation :

type Order struct { ID string Amount int } func (o Order) String() string { return fmt.Sprintf("Order[%s]: %d", o.ID, o.Amount) } func main() { o := Order{"A123", 99} fmt.Println(o) } // Sortie : Order[A123]: 99

Si la méthode est mise en œuvre avec des erreurs ou de manière inadéquate, cela entraîne des logs avec des sorties insensées ou dangereuses, rendant difficile la recherche de bugs.

Question piège

Que se passe-t-il si votre type implémente la méthode String(), mais que vous l'utilisez comme un pointeur, et non comme une valeur ? La méthode String() sera-t-elle appelée ?

Réponse : La méthode String() sera appelée si elle est implémentée pour le type pointeur. Si elle est seulement implémentée pour la valeur, mais que dans le code le type est un pointeur, la méthode sera appelée grâce à l'auto-déréférencement de Go pour les méthodes sans receveur pointeur. En général, ce n'est pas un problème, mais si la méthode est implémentée uniquement pour le receveur pointeur, et que vous l'appelez pour une valeur, cela entraînera une erreur de compilation.

Exemple :

type X struct{} func (x *X) String() string { return "ptr" } fmt.Println(X{}) // erreur : X ne met pas en œuvre String fmt.Println(&X{}) // ok, la méthode sera appelée

Exemples d'erreurs réelles dues à la méconnaissance des subtilités du sujet


Histoire

Dans un grand projet, le type d'erreur a implémenté String() uniquement sur le receveur pointeur (func (err *MyErr) String() string). En conséquence, lors du retour de la valeur d'erreur (non pointeur), les logs affichaient une sortie inutile {} au lieu d'un message utile. Le bug est resté longtemps inaperçu.


Histoire

Un de ces types stockait des données sensibles, et en raison d'une implémentation erronée de String() (affichant les champs au sens littéral), les mots de passe des utilisateurs ont commencé à apparaître dans les logs de production. Un audit de sécurité a été nécessaire.


Histoire

L'utilisation de la génération automatique de String() a conduit à ce qu'à la mise à jour de la structure, le nouveau format de sortie ne corresponde pas à l'ancien, rendant les logs illisibles et perturbant le parsing des logs par des systèmes tiers.