Designing with Interfaces

Overview

Go interfaces enable polymorphism through implicit satisfaction—types implement interfaces by having matching methods, without explicit declarations.

Interface Basics

type Reader interface {
    Read(p []byte) (n int, err error)
}

// Any type with Read method satisfies Reader
type File struct { /* ... */ }
func (f *File) Read(p []byte) (int, error) { /* ... */ }

type Buffer struct { /* ... */ }
func (b *Buffer) Read(p []byte) (int, error) { /* ... */ }

Implicit Satisfaction

type Stringer interface {
    String() string
}

type Person struct {
    Name string
}

// Person implicitly implements Stringer
func (p Person) String() string {
    return p.Name
}

var s Stringer = Person{Name: "Alice"}  // Works!

The Empty Interface

interface{}  // Matches any type
any          // Alias (Go 1.18+)

func print(v any) {
    fmt.Println(v)
}

print(42)
print("hello")
print(true)

Interface Values

An interface value holds a (type, value) pair:

var r io.Reader
r = os.Stdin       // (type: *os.File, value: stdin)
r = &bytes.Buffer{}  // (type: *bytes.Buffer, value: buf)

nil Interface vs nil Value

var r io.Reader    // nil interface (no type, no value)
var f *os.File     // nil pointer

r = f              // Interface holds (*os.File, nil)
r == nil           // false! Has type, value is nil

Common Patterns

Accept Interface, Return Concrete

// Accept interface
func Process(r io.Reader) error { }

// Return concrete type
func NewBuffer() *bytes.Buffer {
    return &bytes.Buffer{}
}

Small Interfaces

type Reader interface {
    Read([]byte) (int, error)
}

type Writer interface {
    Write([]byte) (int, error)
}

type ReadWriter interface {
    Reader
    Writer
}

Assert Interface Compliance

var _ io.Reader = (*MyReader)(nil)  // Compile-time check

Summary

Concept Description
Implicit No implements keyword
any/interface{} Matches all types
Small interfaces Prefer 1-3 methods
Accept interface Flexible function parameters