Lesson 3 · 3 of 20
Embedding and composition · Go
Compose types in Go by embedding structs: promoted fields and methods, overriding a method, and why it is not inheritance. Customers and addresses.

Go has no inheritance; it has composition. Customer includes Contact and Address by writing just the type, with no field name: that is an embedded field. The field exists and is named after its type, c.Contact and c.Address.
The fields and methods of the embedded type are promoted: c.Name is really c.Contact.Name and c.City is c.Address.City. The same goes for methods: c.Label() and c.Line() work as if Customer had declared them, so Customer satisfies any interface that asks for Label() string.
Promotion goes through several levels. VIP embeds Customer, so v.Points += 50 changes v.Customer.Points (from 120 to 170) and v.Contact.Email reaches the contact two levels down.
VIP declares its own Label, which shadows the promoted one: v.Label() adds the tier, [gold]. The original is still there and you call it by name, v.Customer.Label(), which is exactly what the new method does to avoid repeating code.
Careful, this is not inheritance: a VIP is not a Customer and cannot be passed where one is expected; you pass v.Customer. And if two embedded types had a field with the same name, you would have to say which one, c.Contact.Name or c.Address.Name.
Code · main.go
package mainimport "fmt"type Contact struct{ Name, Email string }func (c Contact) Label() string { return c.Name + " <" + c.Email + ">" }type Address struct{ City, Country string }func (a Address) Line() string { return a.City + ", " + a.Country }// Customer embeds Contact and Address: their fields and methods are promoted.type Customer struct { Contact Address Points int}type VIP struct { // embeds Customer and overrides its Label method Customer Tier string}func (v VIP) Label() string { return "[" + v.Tier + "] " + v.Customer.Label() }func main() { c := Customer{Contact{"Ana Ruiz", "ana@shop.es"}, Address{"Seville", "Spain"}, 120} fmt.Println(c.Name, "|", c.Label(), "|", c.Line(), "|", c.Points, "points") v := VIP{Customer: c, Tier: "gold"} v.Points += 50 // promoted through two levels: v.Customer.Points fmt.Println(v.Label(), "|", v.Points, "points") fmt.Println(v.Customer.Label(), "|", v.Contact.Email, "|", v.Line())}Output · go run main.go
Ana Ruiz | Ana Ruiz <ana@shop.es> | Seville, Spain | 120 points [gold] Ana Ruiz <ana@shop.es> | 170 points Ana Ruiz <ana@shop.es> | ana@shop.es | Seville, Spain
Key points
- Embedding means writing the type with no field name.
- Fields and methods of the embedded type are promoted.
- A method of your own with the same name shadows the promoted one.
- The original is still reachable:
v.Customer.Label(). - It is composition, not inheritance: a
VIPis not aCustomer.
Exercise
Add a Postcode field to Address and make Line() include it. Then define a Labeler interface with Label() string and a function that takes a Labeler: pass it c and v and compare what it prints.