ProgrammationDéveloppeur Backend

Comment fonctionne l'imbrication des structures (embedding structs) en Go ? Qu'est-ce qui différencie l'imbriquement de l'inclusion classique dans une structure ? Quelles subtilités doivent être prises en compte ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Go, l'embedding (imbrication) est un mécanisme qui permet de "hériter" du comportement d'une structure en intégrant une structure dans une autre sans utiliser d'héritage explicite, comme dans la POO. La structure imbriquée devient un champ imbriqué (champ anonyme) de la nouvelle structure.

Ainsi, toutes les méthodes de la structure imbriquée deviennent des méthodes de la nouvelle structure, comme si elles étaient "héritées".

Exemple :

package main import "fmt" type Animal struct { Name string } func (a Animal) Speak() { fmt.Println("Mon nom est", a.Name) } type Dog struct { Animal // embedding, champ anonyme Breed string } func main() { d := Dog{ Animal: Animal{Name: "Rex"}, Breed: "Shepherd", } d.Speak() // Méthode héritée ! fmt.Println(d.Name) // Champ accessible directement }

La principale différence entre l'imbriquement et l'inclusion classique d'une structure en tant que champ nommé :

  • En imbrication, les méthodes et champs deviennent accessibles directement dans la nouvelle structure,
  • En inclusion classique, il faut y accéder par le nom du champ.

Question piège

"Si la structure imbriquée et l'extérieur contiennent des champs avec les mêmes noms, lequel sera utilisé et comment y accéder ?"

Beaucoup pensent que seul le champ "supérieur" sera utilisé, mais cela fonctionne autrement :

  • Si les noms coïncident, le niveau externe sera utilisé, et l'interne peut être accédé via le nom de la structure imbriquée.

Exemple :

type Base struct { Name string } type Child struct { Base // embedding Name string // Champ identique } c := Child{Base: Base{Name: "Base"}, Name: "Child"} fmt.Println(c.Name) // Child fmt.Println(c.Base.Name) // Base

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


Histoire

Dans un projet, nous avons imbriqué une structure standard pour le logging, puis défini un champ Logger dans le type enfant. En conséquence, les appels d.Logger.Info() ne fonctionnaient pas comme prévu, car l'accès au champ imbriqué était "perdu" parmi les noms identiques.


Histoire

Un développeur essayait de redéfinir une méthode dans la structure enfant, mais les méthodes ne sont pas "overrides" — une nouvelle apparaît simplement, et la parent est toujours accessible comme Child.Base.Method(). Cela faisait que certains morceaux de code utilisaient la "vieille" version de la méthode, attendant une autre logique.


Histoire

Lors de la sérialisation JSON avec des structures imbriquées, des surprises sont survenues : les champs de la structure "imbriquée" apparaissaient dans l'objet racine. En raison de l'ignorance de l'impact de l'imbriquement sur le marshaling, la structure était sérialisée de manière incorrecte, brisant la rétrocompatibilité de l'API.