Go Interfaces

An interface in Go is a set of method signatures. Any type that has those methods satisfies the interface automatically, with no implements keyword and no declared relationship. A *os.File, a *bytes.Buffer, a network connection, and a gzip stream are all io.Readers simply because each has a Read([]byte) (int, error) method.

This implicit, structural style shapes how Go programs are designed. Interfaces are small, defined by the code that uses them rather than the code that implements them, and composed from one another. It's what makes Go code easy to test and easy to plug together without frameworks.

TL;DR

Quick Example

Consumer-defined interfaces make code testable without mocking frameworks:

The SES client's package never mentions EmailSender. It satisfies it just by having the method.

Core Concepts

Implicit Satisfaction

Decoupling is total: an interface can be declared after the types that satisfy it, even in a different module. To assert at compile time that a type satisfies an interface, a common idiom is var _ io.Writer = (*MyWriter)(nil).

Method Sets and Pointer Receivers

If methods are defined on *T (pointer receiver), only *T satisfies the interface, not T. Methods on T (value receiver) are available to both T and *T. A frequent compile error is "T does not implement X (method has pointer receiver)"; pass &t instead.

Small, Composable Interfaces

The standard library's most powerful interfaces have one method:

Larger interfaces are built by embedding: type ReadWriteCloser interface { Reader; Writer; Closer }. Because so many types speak io.Reader and io.Writer, you can chain them freely, for example streaming an HTTP response through gzip into a file with io.Copy.

Type Assertions and Type Switches

Asserting to another interface is how Go discovers optional capabilities. io.Copy checks whether the source implements io.WriterTo to take a faster path.

The Empty Interface: any

interface{}, now spelled any, has no methods, so every type satisfies it. It's necessary for truly heterogeneous data (JSON decoding into map[string]any, fmt.Println's arguments), but it throws away type safety. Since Go 1.18, generics often replace any in container and utility code.

Design Guidelines

Best Practices

Keep Interfaces Minimal

A function that only reads needs an io.Reader, not an *os.File. Narrow parameter types document intent and widen what callers can pass: files, buffers, network streams, and test fixtures.

Use Fakes Over Mocks

Hand-written fakes that satisfy a small interface are short, readable, and don't break when call order changes. Reach for generated mocks (mockery, gomock) only for large interfaces you don't control.

Check Optional Interfaces Explicitly

When behavior depends on an extra capability (http.Flusher, io.Closer), use the comma-ok assertion and fall back gracefully, rather than asserting without ok and panicking.

Common Mistakes

The Nil Interface Gotcha

An interface value is nil only when both its dynamic type and value are nil. Return a literal nil for "no error", never a typed nil pointer. See Go error handling.

Producer-Side "Header" Interfaces

Export the concrete struct instead, and let each consumer declare the few methods it needs.

Overusing any

A function taking any and type-switching over ten cases is usually a sign you want a proper interface with methods, or generics.

FAQ

How do I know which interfaces a type implements?

Go doesn't record it. Satisfaction is structural and computed by the compiler where needed. Your editor (gopls) can list implementations and implemented interfaces. To guarantee a type satisfies an interface, add a compile-time assertion: var _ Iface = (*T)(nil).

Should I return an interface from a constructor?

Usually not. Return the concrete type so callers get the full API and you can add methods freely. Returning an interface makes sense when the concrete type must stay hidden, as with many implementations behind one factory, or the standard library's errors.New returning error.

What's the difference between interfaces and generics in Go?

Interfaces provide runtime polymorphism: one variable can hold different concrete types, and method calls dispatch dynamically. Generics provide compile-time polymorphism: a function is written once and instantiated per type, with full type safety for containers and algorithms. They complement each other, and generic constraints are themselves written as interfaces.

Is any the same as interface{}?

Yes. any is an alias for interface{}, added in Go 1.18 for readability.

Related Topics

References