ProgrammingBackend Developer

How does embedding structs work in Go? What is the difference between embedding and regular struct inclusion? What nuances should be considered?

Pass interviews with Hintsage AI assistant

Answer

In Go, embedding is a mechanism that allows you to "inherit" the behavior of a struct by embedding one struct within another without using explicit inheritance, as in OOP. The embedded struct becomes anonymous fields of the new struct.

In this way, all methods of the embedded struct become methods of the new struct, as if they were "inherited".

Example:

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, anonymous field Breed string } func main() { d := Dog{ Animal: Animal{Name: "Rex"}, Breed: "Shepherd", } d.Speak() // Inherited method! fmt.Println(d.Name) // Field is directly accessible }

The main difference between embedding and regular struct inclusion as a named field is:

  • In embedding, methods and fields become directly accessible on the new struct,
  • In regular inclusion, you need to access them through the field name.

Trick Question

"If the embedded struct and the outer struct contain fields with the same names, which one will be used and how to access them?"

Many believe that only the "outer" field will be used, but it works differently:

  • If names collide, the outer level will be used, and the inner one can be accessed through the name of the embedded struct.

Example:

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

Examples of Real Errors Due to Ignorance of the Nuances of the Topic


Story

In a project, the standard struct for logging was embedded, and then a Logger field was defined in the child type. As a result, calls to d.Logger.Info() did not work as expected because access to the embedded field was "lost" among the similarly named fields.


Story

A developer tried to override a method in the child struct, but methods do not "override" — a new one simply appears, and the parent method is still accessible as Child.Base.Method(). This led to some code using the "old" version of the method, expecting different logic.


Story

During JSON serialization with embedded structs, unexpected results occurred: fields of the "embedded" struct appeared in the root object. Due to ignorance of how embedding affects marshaling, the struct was serialized incorrectly, breaking API backward compatibility.