Struct Embedding
Most languages let one class inherit from another. Go doesn't have inheritance at all — and yet you still want to reuse a type's fields and behavior. The tool for that is embedding: you drop a type into a struct as an anonymous field, with no field name, just the type:
package main
import "fmt"
type Animal struct {
Name string
Legs int
}
type Dog struct {
Animal // embedded: anonymous field, no name
Breed string
}
func main() {
d := Dog{
Animal: Animal{Name: "Rex", Legs: 4},
Breed: "Terrier",
}
fmt.Println(d.Name, d.Legs, d.Breed) // Rex 4 Terrier
}
Notice the field declaration is just Animal, not animal Animal. That's what
makes it embedded rather than an ordinary named field.
Fields are promoted
Because the field is anonymous, the compiler promotes the embedded struct's
fields to the outer type. You write d.Name, not d.Animal.Name. It reads as
if Dog grew the fields itself:
d.Name = "Buddy" // sets d.Animal.Name
fmt.Println(d.Legs)
Promotion is a convenience the compiler provides — under the hood, d.Name is
literally shorthand for d.Animal.Name.
The embedded value is still there by name
Embedding doesn't melt Animal into Dog. The embedded value is a real field,
reachable by its type name:
fmt.Println(d.Animal.Name) // Rex — the long way, still valid
inner := d.Animal // pull the whole Animal back out
This matters when a name collides. If Dog had its own Name field, d.Name
would mean the outer one, and d.Animal.Name would reach the embedded one — no
ambiguity, because you can always qualify.
This is composition, not inheritance
A Dog has an Animal; it is not a subtype of Animal. There's no class
hierarchy, no super, no base-class constructor to chain. You're gluing
independent pieces together, and each piece stays a complete, usable type on
its own.
Why did Go's designers leave inheritance out? Because deep inheritance trees are
a well-worn source of pain: fragile base classes, behavior that changes three
levels up, the "is a Manager really an Employee really a Person?" debates.
Composition sidesteps all of it. You assemble types from smaller ones, the
relationships stay shallow and explicit, and when requirements change you swap a
part rather than re-parent a hierarchy. The famous advice "favor composition
over inheritance" isn't a suggestion in Go — it's the only option, and the
language is simpler for it.
Try it
The exercise gives you a Person struct. You'll build an Employee that embeds
it and adds a salary — then watch the promoted fields (e.Name, e.Age) work
right alongside e.Salary.
Practice
1. Employee embeds Person
IntermediateThe starter defines a plain
Personstruct:type Person struct { Name string Age int }Employeeis already declared for you — it embedsPersonas an anonymous field and adds aSalary:type Employee struct { Person Salary int }Implement the constructor so it fills every field:
func newEmployee(name string, age, salary int) EmployeeGiven
newEmployee("Ada", 36, 50000), the result must satisfy:e.Name == "Ada"ande.Age == 36(promoted from the embeddedPerson)e.Salary == 50000e.Person.Name == "Ada"(the embedded value, reached by its type name)
Build the
Employeewith a composite literal. The embedded field is written using the type name as the key:Employee{Person: Person{...}, Salary: ...}.package main import "fmt" // Person is a plain struct with two fields. type Person struct { Name string Age int } // Employee embeds Person as an anonymous field, so Person's fields are // promoted (e.Name, e.Age), and adds a Salary of its own. type Employee struct { Person Salary int } // newEmployee builds an Employee from the given values. func newEmployee(name string, age, salary int) Employee { // TODO: implement return Employee{} } func main() { e := newEmployee("Ada", 36, 50000) fmt.Println(e.Name, e.Age, e.Salary) }Sign in to write and run your solution in the browser.
Sign in for hints and the solution.