Go's duck typing: implicitly satisfied contracts.
Interface Basics
An interface is a list of method signatures. Any type that has those methods satisfies the interface — automatically, without declaring "I implement X".
This is "duck typing" with compile-time checks. It's the cleanest part of Go.
Defining an interface
type Animal interface {
Speak() string
}
Any type that has a Speak() string method is — at the type-system level — an
Animal. No implements keyword, no inheritance.
Implicit satisfaction
Dog and Cat never mention Animal. The compiler checks the method set at the
call site.
A real-world example: io.Writer
io.Writer is one method:
type Writer interface {
Write(p []byte) (n int, err error)
}
Everything in Go that writes bytes is an io.Writer — files, network sockets,
buffers, HTTP responses. Once you write a function that takes io.Writer, it
works with all of them.
Keep interfaces small
The Go proverb says: "the bigger the interface, the weaker the abstraction." The smallest interfaces (one or two methods) are the most reusable. Define them on the consumer side — i.e. write the interface where you use it, not where you implement it.
The nil-interface gotcha
An interface value has two parts: a type and a value. It's nil only when both are nil. This bites people:
func boom() error {
var p *MyError = nil
return p // returns a non-nil error whose value is nil!
}
if err := boom(); err != nil { // ← this is TRUE
log.Println(err)
}
The interface knows about the type *MyError, even though the value is nil — so
err == nil is false. Fix: return nil directly when there's no error.