Implicit Interface Satisfaction
Go interfaces are implicitly satisfied. There's no implements keyword. If a type has all the methods an interface requires, it satisfies that interface automatically. This is Go's most powerful design decision — it enables decoupling without forcing type hierarchies.
// Define an interface
type Shape interface {
Area() float64
Perimeter() float64
}
// Circle satisfies Shape (no "implements" needed)
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
// Rectangle also satisfies Shape
type Rect struct{ Width, Height float64 }
func (r Rect) Area() float64 { return r.Width * r.Height }
func (r Rect) Perimeter() float64 { return 2 * (r.Width + r.Height) }
// Function accepts any Shape
func printShapeInfo(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
printShapeInfo(Circle{Radius: 5})
printShapeInfo(Rect{Width: 10, Height: 5})
Output
Area: 78.54, Perimeter: 31.42 Area: 50.00, Perimeter: 30.00
Key Takeaway: Define interfaces where they're used, not where they're implemented. Keep interfaces small — the standard library's most powerful interfaces have 1-2 methods (
io.Reader, io.Writer, error, fmt.Stringer).Key Standard Library Interfaces
| Interface | Methods | Used By |
|---|---|---|
io.Reader | Read(p []byte) (n int, err error) | Files, HTTP bodies, buffers, compression |
io.Writer | Write(p []byte) (n int, err error) | Files, HTTP responses, buffers |
fmt.Stringer | String() string | Custom print formatting |
error | Error() string | All error handling |
sort.Interface | Len(), Less(i,j), Swap(i,j) | Custom sorting |
Empty Interface and Type Assertions
// any (alias for interface{}) accepts ANY type
func printType(v any) {
// Type switch
switch val := v.(type) {
case int:
fmt.Printf("Integer: %d\n", val)
case string:
fmt.Printf("String: %q\n", val)
default:
fmt.Printf("Unknown: %v\n", val)
}
}
printType(42)
printType("hello")
printType(3.14)
// Type assertion (single type check)
var i any = "hello"
s, ok := i.(string) // comma-ok pattern
if ok {
fmt.Println("String value:", s)
}
Output
Integer: 42 String: "hello" Unknown: 3.14 String value: hello
⚠️ Common Mistake: Overusing interface{}/any
Using any everywhere defeats the purpose of Go's type system. Use specific interfaces or generics instead. any should be a last resort, not a default.
Interface Satisfaction (Implicit!)
io.Reader
Read([]byte) (int, error)
Read([]byte) (int, error)
io.Writer
Write([]byte) (int, error)
Write([]byte) (int, error)
fmt.Stringer
String() string
String() string
error
Error() string
Error() string
sort.Interface
Len, Less, Swap
Len, Less, Swap
http.Handler
ServeHTTP(w, r)
ServeHTTP(w, r)
Practice Exercises
Medium Build a Mini Project
Combine concepts from this tutorial to build a small utility or tool.
Medium Debug Challenge
Introduce a bug in one of the code examples and practice finding and fixing it.
Hard Refactoring Exercise
Rewrite one example using a different approach and compare the tradeoffs.