Skip to main content
eLearner.app
Module 5 · Lesson 3 of 523/50 in the course~12 min
Module lessons (3/5)

Composition (embedding)

Go has no inheritance. The idiomatic answer to reuse is composition, and in particular embedding: including one type inside another without giving it a field name. The fields and methods of the embedded type get promoted, becoming directly accessible as if they belonged to the outer type.

Struct embedding

Go
type Animal struct {
    Name string
}

func (a Animal) Greet() string {
    return "ciao da " + a.Name
}

type Dog struct {
    Animal        // embedding — NO field name
    Breed string
}

c := Dog{
    Animal: Animal{Name: "Rex"},
    Breed:  "Pastore",
}

fmt.Println(c.Name)         // "Rex" — promoted from Animal
fmt.Println(c.Greet())      // "ciao da Rex" — promoted method
fmt.Println(c.Breed)        // own field

The embedded field is still accessible by the type's name: c.Animal.Name works and is identical to c.Name. It is needed when there are collisions.

Override (partial): method "shadowing"

If the outer type defines a method with the same name as the promoted one, its method "wins". From inside you can always reach the embedded one via the type's name:

Go
func (c Dog) Greet() string {
    return "BAU! " + c.Animal.Greet()
}

It is not polymorphic inheritance: it is just lexical name resolution. For polymorphism you use interfaces (Module 6).

Multiple embedding

You can embed multiple types:

Go
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }

type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }

type Service struct {
    Logger
    Timer
    Name string
}

s := Service{Name: "api"}
s.Log("start")   // promoted from Logger
s.Now()          // promoted from Timer

If two embedded types have a method with THE SAME name, direct access becomes ambiguous and the compiler requires the explicit form (s.Logger.Foo() or s.Timer.Foo()).

Interface embedding

Interfaces can also be "embedded" (see Module 6):

Go
type ReadWriter interface {
    io.Reader
    io.Writer
}

It is equivalent to the union of Reader's and Writer's methods.

When to use explicit composition (not embedding)

If you want a named field and no auto-promotion, use plain composition:

Go
type Dog struct {
    Anim  Animal    // NORMAL field, no promotion
    Breed string
}
c.Anim.Name   // you need the field name

Embedding is a handy delegation tool, not a design choice strictly better than explicit composition: use it when you really want to promote the inner type's API.

Try it

Exercise#go.m5.l3.e1
Attempts: 0Loading…

Define Dog which embeds Animal (embedding, no field name) and has a Breed string field.

Loading editor…
Show hint

Embedding: write the TYPE without giving it a field name.

Solution available after 3 attempts

Exercise#go.m5.l3.e2
Attempts: 0Loading…

Add a Greet() string method to Animal and call it directly on a Dog variable (method promotion).

Loading editor…
Show hint

The method defined on Animal is promoted to Dog thanks to embedding.

Solution available after 3 attempts

Quiz#go.m5.l3.e3
Ready

Does Go support classical inheritance?

Go
type B struct { /* extends A? */ }
Answer options

Recap

  • No inheritance: you compose with embedding.
  • Embedding = writing the TYPE without a field name in the struct.
  • Fields and methods of the embedded type get promoted.
  • Explicit access is always available: outer.Inner.Field.
  • "Lexical" override: the outer type's method shadows the promoted one.
  • Embedding ≠ is-a: for polymorphism use interfaces.