Go by Example: Embedded Fields
Go uses struct embedding to achieve composition over inheritance. This example demonstrates how embedded fields work, method promotion, and how to access shadowed fields.
Code
package main
import "fmt"
// 1. Base Struct
type User struct {
Name string
Email string
}
// Method on User
func (u *User) Notify() {
fmt.Printf("Sending email to %s <%s>\n", u.Name, u.Email)
}
// 2. Embedding Struct
// Admin "is a" User (composition)
type Admin struct {
User // Embedded field (no name)
Level string
}
func main() {
// 3. Initialization
// We must initialize the embedded struct explicitly.
ad := Admin{
User: User{
Name: "System Admin",
Email: "[email protected]",
},
Level: "Superuser",
}
// 4. Field Promotion
// We can access Name directly on Admin, even though it's in User.
fmt.Println("Name:", ad.Name)
fmt.Println("Level:", ad.Level)
// 5. Method Promotion
// Admin automatically gets the Notify method from User.
ad.Notify()
// 6. Accessing the embedded field directly
// Useful if there's a name collision (shadowing).
fmt.Println("Inner Email:", ad.User.Email)
}
Explanation
Go eschews traditional class-based inheritance in favor of composition via struct embedding, a powerful mechanism that allows a struct to include another struct as an anonymous field, thereby inheriting its fields and methods. When a type is embedded, its fields and methods are "promoted" to the outer struct, enabling direct access as if they were defined on the outer struct itself; this facilitates code reuse and modular design while maintaining a flat and transparent type hierarchy. This approach aligns with the "is-a" relationship concept but implements it through containment, giving developers the flexibility to compose complex behaviors from smaller, focused types without the fragility often associated with deep inheritance trees.
Method promotion is a key consequence of embedding, where methods defined on the inner type become part of the outer type's method set, allowing interfaces satisfied by the inner type to be automatically satisfied by the outer type as well. However, Go resolves naming conflicts through explicit rules: if the outer struct defines a field or method with the same name as one in the embedded struct, the outer one "shadows" the inner one. In such cases, the shadowed member remains accessible by explicitly referencing the embedded type's name, ensuring that ambiguity is resolved deterministically and that the developer retains full control over the struct's behavior.
- Composition: Build complex types from simple ones.
- Promotion: Inner fields/methods appear on the outer struct.
- Shadowing: Outer fields override inner ones with same name.
- Interfaces: Outer struct satisfies interfaces of embedded struct.

