Skip to content

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.

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.

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 interface{} (or any in Go 1.18+) has no methods, so every type satisfies it. Use it sparingly - it trades compile-time safety for flexibility.

When you have an interface value, you often need to access the underlying concrete type. Type assertions let you do this safely.

Type switches are a cleaner way to handle multiple possible types:

A type satisfies an interface if it implements all required methods with matching signatures. The compiler checks this automatically.

Methods can have pointer or value receivers. This affects interface satisfaction:

  1. Implicit satisfaction - Types implement interfaces automatically by having the right methods
  2. Composition over inheritance - Embed interfaces to build up behaviors
  3. Empty interface sparingly - any sacrifices type safety; prefer specific interfaces
  4. Type assertions safely - Always use the two-value form v, ok := i.(T)
  5. Type switches - Cleaner than chains of type assertions
  6. Pointer receivers matter - *T and T have different interface satisfaction rules

Shape Calculator

medium

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.


Chapter in progress
0 / 14 chapters completed

Next up: Chapter 2: Error Handling Patterns