Chapter 1: Interfaces & Type System
Go’s interface system is one of its most powerful features. Unlike many languages, Go interfaces are implicitly satisfied - there’s no implements keyword. A type satisfies an interface simply by implementing all of its methods.
This implicit satisfaction is a fundamental design choice. In languages like Java, you must explicitly declare that a class implements an interface. In Go, if your type has the right methods, it satisfies the interface automatically. This enables loose coupling and makes it easy to adapt existing types to new interfaces.
Interfaces are Go’s only abstraction mechanism. No classes, no inheritance, no generics (until Go 1.18). Just interfaces and composition. This simplicity forces clear thinking about abstractions and leads to flexible, maintainable code.
Interface Basics
Section titled “Interface Basics”Understanding Implicit Satisfaction
Section titled “Understanding Implicit Satisfaction”An interface defines a set of method signatures. Any type that implements those methods automatically satisfies the interface. You never declare intent to implement an interface - if the methods match, the type satisfies it.
This decoupling is powerful. A library can define an interface, and your existing types can satisfy it without modification. You can define interfaces for types you don’t own. Standard library interfaces like io.Reader work with countless third-party types, all without explicit coupling.
Interface Composition
Section titled “Interface Composition”Interfaces can embed other interfaces, creating more specific contracts. This is a powerful way to build up complex behaviors from simple parts.
The Empty Interface
Section titled “The Empty Interface”The empty interface has no methods, so every type satisfies it. Since Go 1.18 it is spelled any, which is a predeclared alias for interface{}. The two are identical to the compiler, but any is the idiomatic spelling in new code - you will only see interface{} in code written before 1.18. Use the empty interface sparingly either way: it trades compile-time safety for flexibility.
Type Assertions
Section titled “Type Assertions”When you have an interface value, you often need to access the underlying concrete type. Type assertions let you do this safely.
Type Switches
Section titled “Type Switches”Type switches are a cleaner way to handle multiple possible types:
Interface Satisfaction
Section titled “Interface Satisfaction”A type satisfies an interface if it implements all required methods with matching signatures. The compiler checks this automatically.
Pointer vs Value Receivers
Section titled “Pointer vs Value Receivers”Methods can have pointer or value receivers. This affects interface satisfaction:
Key Takeaways
Section titled “Key Takeaways”- Implicit satisfaction - Types implement interfaces automatically by having the right methods
- Composition over inheritance - Embed interfaces to build up behaviors
- Empty interface sparingly -
anysacrifices type safety; prefer specific interfaces - Type assertions safely - Always use the two-value form
v, ok := i.(T) - Type switches - Cleaner than chains of type assertions
- Pointer receivers matter -
*TandThave different interface satisfaction rules
Exercise
Section titled “Exercise”Shape Calculator
Create a Shape interface with Area() and Perimeter() methods. Implement it for Rectangle and Circle types. Then write a function that takes a Shape and prints both its area and perimeter.
Practice
Section titled “Practice”Interfaces are the seam that makes Go code testable: you depend on a small interface, and swap in a fake when you test. Work through the testing battery to see that play out:
- Testing Exercises - especially 2. Mocking with Interfaces, which is this chapter applied directly
- Generics Exercises - the modern alternative to
anywhen you want type safety back
Next up: Chapter 2: Error Handling Patterns