Go Struct Embedding
Composition and inheritance-like behavior in Go
🧩 What is Struct Embedding?
Struct embedding allows you to include one struct inside another, promoting fields and methods. It provides composition-based inheritance, enabling code reuse and creating complex types from simpler ones elegantly.
// Base struct
type Person struct {
Name string
Age int
}
// Embedded struct
type Employee struct {
Person // Embedded field
JobTitle string
Salary int
}
Usage:
emp.Name = "John" // Direct access to embedded field
Key Embedding Concepts
Field Promotion
Embedded fields become direct fields
emp.Name // Instead of emp.Person.Name
Method Promotion
Embedded methods become available
emp.Greet() // Person's method
Composition
Build complex types from simple ones
type Manager struct {
Employee
TeamSize int
}
Interface Satisfaction
Embedded types satisfy interfaces
var speaker Speaker = Employee{}
🔹 Basic Struct Embedding
Simple example of embedding one struct in another:
type Address struct {
Street string
City string
State string
}
func (a Address) FullAddress() string {
return fmt.Sprintf("%s, %s, %s", a.Street, a.City, a.State)
}
type Person struct {
Name string
Age int
Address // Embedded struct
}
func (p Person) Introduce() string {
return fmt.Sprintf("Hi, I'm %s, %d years old", p.Name, p.Age)
}
func main() {
person := Person{
Name: "Alice",
Age: 30,
Address: Address{
Street: "123 Main St",
City: "Boston",
State: "MA",
},
}
// Direct access to embedded fields
fmt.Println(person.Name) // Alice
fmt.Println(person.Street) // 123 Main St (promoted field)
fmt.Println(person.FullAddress()) // 123 Main St, Boston, MA (promoted method)
fmt.Println(person.Introduce()) // Hi, I'm Alice, 30 years old
}
Output:
Alice
123 Main St
123 Main St, Boston, MA
Hi, I'm Alice, 30 years old
🔹 Multiple Embedding
Embedding multiple structs in one:
type Contact struct {
Email string
Phone string
}
func (c Contact) GetContact() string {
return fmt.Sprintf("Email: %s, Phone: %s", c.Email, c.Phone)
}
type Skills struct {
Programming []string
Languages []string
}
func (s Skills) ListSkills() string {
return fmt.Sprintf("Programming: %v, Languages: %v", s.Programming, s.Languages)
}
type Developer struct {
Person // First embedding
Contact // Second embedding
Skills // Third embedding
Level string
}
func main() {
dev := Developer{
Person: Person{
Name: "Bob",
Age: 28,
Address: Address{
Street: "456 Tech Ave",
City: "San Francisco",
State: "CA",
},
},
Contact: Contact{
Email: "[email protected]",
Phone: "555-0123",
},
Skills: Skills{
Programming: []string{"Go", "Python", "JavaScript"},
Languages: []string{"English", "Spanish"},
},
Level: "Senior",
}
// Access fields from all embedded structs
fmt.Println(dev.Name) // From Person
fmt.Println(dev.Email) // From Contact
fmt.Println(dev.Programming) // From Skills
fmt.Println(dev.Level) // Own field
// Call methods from embedded structs
fmt.Println(dev.Introduce()) // Person method
fmt.Println(dev.GetContact()) // Contact method
fmt.Println(dev.ListSkills()) // Skills method
}
Output:
Bob
[email protected]
[Go Python JavaScript]
Senior
Hi, I'm Bob, 28 years old
Email: [email protected], Phone: 555-0123
Programming: [Go Python JavaScript], Languages: [English Spanish]
🔹 Method Overriding
Override embedded methods by defining your own:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return a.Name + " makes a sound"
}
func (a Animal) Move() string {
return a.Name + " moves"
}
type Dog struct {
Animal
Breed string
}
// Override the Speak method
func (d Dog) Speak() string {
return d.Name + " barks loudly!"
}
// Animal's Move method is still available
func main() {
dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}
fmt.Println(dog.Speak()) // Dog's overridden method
fmt.Println(dog.Move()) // Animal's method (not overridden)
fmt.Println(dog.Animal.Speak()) // Access original method explicitly
}
Output:
Buddy barks loudly!
Buddy moves
Buddy makes a sound
🔹 Interface Embedding
Embed interfaces to create larger interfaces:
type Reader interface {
Read() string
}
type Writer interface {
Write(string)
}
// Embed interfaces
type ReadWriter interface {
Reader
Writer
}
type File struct {
content string
}
func (f *File) Read() string {
return f.content
}
func (f *File) Write(data string) {
f.content = data
}
func ProcessFile(rw ReadWriter) {
rw.Write("Hello, World!")
fmt.Println("File content:", rw.Read())
}
func main() {
file := &File{}
ProcessFile(file) // File implements ReadWriter through embedding
}
Output:
File content: Hello, World!