Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Maps & Structs  ›  Capstone Project

Embedding Structs

Embedding Structs 16 minEmbedding a struct type as an unnamed field promotes its fields and methods to the outer struct, which is composition rather than inheritance: the outer type contains the inner one but is not a kind of it.

Embedding Structs

tally reports on one file at a time, but a new feature will summarise many files. Both reports need the same core numbers — total words, unique words — plus their own extras. Rather than copy those fields into every struct, Go lets you embed one struct inside another.

The rule

Embed to contain: inner fields and methods are promoted, but it is not inheritance.

type Stats struct {
	Total  int `json:"total"`
	Unique int `json:"unique"`
}

func (s Stats) Repeats() int { return s.Total - s.Unique }

type Report struct {
	Stats             // embedded: a field with no name
	File string `json:"file"`
}

An embedded field is written as just a type. Its field name is the type's name, Stats.

Promoted. The inner fields and methods can be reached as if they belonged to the outer struct:

r := Report{Stats: Stats{Total: 17, Unique: 12}, File: "notes.txt"}
fmt.Println(r.Total)     // 17, short for r.Stats.Total
fmt.Println(r.Repeats()) // 5,  short for r.Stats.Repeats()

Note the literal: you still build the embedded part by its type name, Stats: Stats{...}.

Not inheritance. A Report contains a Stats; it is not a kind of Stats. A function show(s Stats) will not accept r — you pass r.Stats. (Promoted methods do let Report satisfy interfaces that Stats satisfies, which you will meet with interfaces.)

Embedding many types is allowed. If two embedded structs at the same depth both have a Total, writing r.Total becomes a compile error (ambiguous selector), and you must name the one you mean, such as r.Stats.Total.

Name clashes. If Report declares its own Total, the outer field wins: r.Total is Report's, and the inner one is still r.Stats.Total.

JSON flattens. encoding/json treats the promoted fields as if they were declared in the outer struct:

data, _ := json.Marshal(r)
fmt.Println(string(data))
// {"total":17,"unique":12,"file":"notes.txt"}

The shortcut

To resolve r.Something, search from the outside in: the outer struct first, then each embedded struct. The first, shallowest match wins.

Resolving names on a ReportStep 1 of 4
  1. r.File

    Found on Report itself. Done.

Drills

Answer fast — trust the rule.

quick drill

With r := Report{Stats: Stats{Total: 17, Unique: 12}}, what is r.Unique?

quick drill

func show(s Stats) exists. Which call compiles?

quick drill

Report gains its own field Total int, set to 99, while r.Stats.Total is 17. What is r.Total?

quick drill

What does json.Marshal print for Report{Stats: Stats{Total: 3, Unique: 2}, File: "a.txt"}?

Remember

  • Embed to contain: inner fields and methods are promoted, but it is not inheritance.
  • Shortcut: resolve names from the outside in; the shallowest match wins.
  • Trap: a Report is not a Stats — pass r.Stats where a Stats is needed.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainReport contains Stats

tally has type Stats struct { Total int; Unique int } with a method Repeats() returning Total - Unique, and type Report struct { Stats; File string }. Explain what r.Total and r.Repeats() do on a Report, why func show(s Stats) cannot be called as show(r), and what changes if Report gains its own Total field.