ProgramaciónDesarrollador Backend

¿Cómo funciona la inclusión de estructuras (embedding structs) en Go? ¿En qué se diferencia la inclusión de una inclusión normal en una estructura? ¿Qué matices deben tenerse en cuenta?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En Go, el embedding (inclusión) es un mecanismo que permite "heredar" el comportamiento de una estructura al incluir una estructura dentro de otra sin el uso de la herencia explícita, como en OOP. La estructura incluida se convierte en un campo-anónimo de la nueva estructura.

En este caso, todos los métodos de la estructura incluida se convierten en métodos de la nueva estructura, como si se "heredaran".

Ejemplo:

package main import "fmt" type Animal struct { Name string } func (a Animal) Speak() { fmt.Println("My name is", a.Name) } type Dog struct { Animal // embedding, campo anónimo Breed string } func main() { d := Dog{ Animal: Animal{Name: "Rex"}, Breed: "Shepherd", } d.Speak() // ¡Método heredado! fmt.Println(d.Name) // Campo accesible directamente }

La principal diferencia entre la inclusión y la inclusión normal de una estructura como campo nombrado es:

  • Con la inclusión, los métodos y campos están disponibles directamente en la nueva estructura,
  • Con la inclusión normal, se requiere acceder a ellos a través del nombre del campo.

Pregunta capciosa

"Si la estructura incluida y la externa contienen campos con los mismos nombres, ¿cuál se utilizará y cómo se accederá a ellos?"

Muchos creen que solo se usará el campo "superior", pero funciona de otra manera:

  • Si los nombres coinciden, se utilizará el nivel externo, y el interno se puede acceder a través del nombre de la estructura incluida.

Ejemplo:

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

Ejemplos de errores reales debido a la falta de conocimiento sobre los matices del tema


Historia

En el proyecto se incluyó una estructura estándar para registros, y luego se definió el campo Logger en el tipo hijo. Como resultado, las llamadas d.Logger.Info() no funcionaban como se esperaba, ya que el acceso al campo incluido se "perdía" entre los de nombre similar.


Historia

El desarrollador intentaba sobrescribir un método en la estructura hija, pero los métodos no se "sobrescriben"; simplemente aparece uno nuevo, y el padre sigue estando disponible como Child.Base.Method(). Esto llevó a que parte del código usara la "antigua" versión del método, esperando otra lógica.


Historia

Al serializar JSON con estructuras anidadas, surgieron sorpresas: los campos de la estructura "incluida" aparecían en el objeto raíz. Debido a la falta de conocimiento sobre cómo la inclusión afecta al marshaling, la estructura se serializaba incorrectamente, rompiendo la compatibilidad con versiones anteriores de la API.