Go Structs, Interfaces & Composition – Build Reusable and Type-Safe Programs
Introduction – Why Use Structs & Interfaces in Go?
Go is a statically typed language without classical inheritance. Instead, it promotes composition over inheritance through powerful features like structs, interfaces, and embedding. These allow you to build flexible, reusable, and type-safe applications while avoiding complexity.
In this guide, you’ll learn:
- How to define and use structs in Go
- How interfaces enable polymorphism and abstraction
- What type assertions are and how to use them safely
- How Go supports composition-based reuse (struct embedding)
Topics Covered
| Concept | Description |
|---|---|
| Go Structs | Custom data types that group related fields |
| Go Interfaces | Define method sets for polymorphic behavior |
| Go Type Assertion | Safely convert an interface value back to its concrete type |
| Go Inheritance via Composition | Simulate inheritance using struct embedding |
Go – Structs
Define & Use
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p.Name) // Alice
}
Update Struct Fields
p.Age = 31
Structs are passed by value. Use pointers to modify them inside functions:
func update(p *Person) {
p.Age = 40
}
Go – Interfaces
An interface defines behavior without implementation:
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
Interface in Action
func printArea(s Shape) {
fmt.Println("Area:", s.Area())
}
Any type that implements all methods of an interface implicitly satisfies it—no need for implements.
Go – Type Assertion
Type assertions let you access the underlying type of an interface:
var i interface{} = "Hello"
s := i.(string) // assert it's a string
fmt.Println(s)
Safe Assertion
val, ok := i.(string)
if ok {
fmt.Println("It's a string:", val)
} else {
fmt.Println("Not a string")
}
Prevents panic on invalid assertions.
Go – Inheritance via Composition (Struct Embedding)
Go doesn’t support classical OOP inheritance. Instead, it uses embedding:
type Animal struct {
Name string
}
func (a Animal) Speak() {
fmt.Println(a.Name, "makes a sound")
}
type Dog struct {
Animal
Breed string
}
Use Embedded Methods
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
d.Speak() // Rex makes a sound
Fields and methods of the embedded struct are promoted to the outer struct.
Summary – Recap & Next Steps
Go’s struct-interface system promotes clarity, safety, and reusability. Instead of inheritance, you build powerful programs through interface abstraction and composition, keeping your code loosely coupled and easily testable.
Key Takeaways:
- Structs group fields under custom types
- Interfaces enable polymorphism without OOP complexity
- Type assertions safely extract values from interfaces
- Composition (struct embedding) simulates inheritance in Go
Real-World Applications:
- Defining request/response models in APIs
- Implementing strategy or behavior patterns
- Building plugin-like systems via interface injection
- Mocking and testing with interfaces
Frequently Asked Questions
Does Go support inheritance like Java or C++?
No. Go encourages composition over inheritance using struct embedding.
Can a struct implement multiple interfaces?
Yes. A struct can implement as many interfaces as needed:
type Reader interface { Read() }
type Writer interface { Write() }
type File struct{}
func (f File) Read() {}
func (f File) Write() {}
What’s the zero value of an interface?
nil. An interface without any value or method is nil.
How do I test if a value implements an interface?
Use:
var s Shape = Circle{Radius: 5}
_, ok := interface{}(s).(Shape)
Can I embed multiple structs in one struct?
Yes. Embedded fields are promoted:
type Engine struct{}
type Wheels struct{}
type Car struct {
Engine
Wheels
}
Share Now :
