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.
r.File
Found on Report itself. Done.
Drills
Answer fast — trust the rule.
With r := Report{Stats: Stats{Total: 17, Unique: 12}}, what is r.Unique?
func show(s Stats) exists. Which call compiles?
Report gains its own field Total int, set to 99, while r.Stats.Total is 17. What is r.Total?
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.Statswhere a Stats is needed.
