Interfaces · 3 MIN READ

Embedding is not inheritance

Predict which method runs when an embedded type has one too.

Writing a type inside a struct without a field name embeds it, and its methods are promoted to the outer type. It looks like inheritance and is not: there is no virtual dispatch and no super. The outer type gets a field it can reach through, and the promoted methods still run against that inner value.

The consequence people trip on: a method the outer type declares shadows the promoted one for outside callers, but a promoted method calling a sibling still calls the inner one. Go resolves this at compile time by name, not at run time by receiver.

SEE THE IDEA

Promotion makes fields and methods easier to reach

  1. 01

    Embedded field

    go
    type Outer struct { Inner }

    Outer contains an Inner value

  2. 02

    Promotion

    go
    outer.Run()

    A convenient path to Inner.Run

  3. 03

    Explicit path

    go
    outer.Inner.Run()

    The contained value is still there

Embedding is composition with promoted selectors.

In practice

Use embedding to expose a contained type deliberately, not to inherit an identity. The embedded value remains a field with its own receiver and state. A field or method on the outer type can hide a promoted selector; ambiguous promoted names must be reached through the explicit field path.

Watch out

Outer is not assignable to Inner just because Inner is embedded in it. Promoted methods can nevertheless satisfy interfaces.

Official reference ↗

Read and predict

go
package main

import "fmt"

type Base struct{ Name string }

func (b Base) Describe() string { return "base " + b.Name }
func (b Base) Greet() string    { return "hello from " + b.Describe() }

type Loud struct{ Base }

func (l Loud) Describe() string { return "LOUD " + l.Name }

func main() {
	l := Loud{Base{Name: "ada"}}
	fmt.Println(l.Describe())
	fmt.Println(l.Greet())
}

What does this print? Look carefully at the second line.

Try it yourself

Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.

Progress stays in this browser. No account needed.