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!

🧠 Test Your Knowledge

What happens when you embed a struct in Go?