Concept · Go
Implicit interfaces
A Go type satisfies an interface just by having the methods — so define small interfaces where you use them.
In Java or C#, a class says implements Reader. Go has no such keyword. A type satisfies an interface automatically if it has the interface's methods with matching signatures. The compiler checks it wherever the value is used as that interface.
type Reader interface {
Read(p []byte) (n int, err error)
}
// *os.File, *bytes.Buffer, *gzip.Reader, net.Conn and http.Response.Body
// all satisfy io.Reader, and none of them mention it.
func countBytes(r io.Reader) (int64, error) {
return io.Copy(io.Discard, r)
}Consequences
Define interfaces where you use them, not where you implement them. The consumer decides what it needs:
// In the package that sends welcome emails, not in the user store package
type userGetter interface {
GetUser(ctx context.Context, id int64) (User, error)
}
func SendWelcome(ctx context.Context, users userGetter, id int64) error { /* ... */ }Your *PostgresStore with its 20 methods satisfies userGetter without knowing it exists. In tests, a fake with one method does too. No mocking framework is needed.
Keep interfaces small. The standard library's most useful interfaces have one method: io.Reader, io.Writer, fmt.Stringer, error and http.Handler. Small interfaces are easy to satisfy and easy to fake, and they compose: io.ReadWriter is just both.
Accept interfaces, return structs. Functions take the narrowest interface they need and return concrete types, so callers get every method without a type assertion.
Gotchas
- Don't create an interface before you have two implementations, or a test that needs one. A
UserServiceinterface mirroring a singleuserServicestruct only adds indirection. - Pointer vs value receivers. If methods have pointer receivers (
func (s *Store) Get…), only*Storesatisfies the interface, notStore. - A nil pointer in an interface isn't a nil interface. Returning a typed
(*MyErr)(nil)as anerrorgives a non-nil error. Return a literalnil. This matters most for errors, sinceerroris itself an interface. - Assert satisfaction at compile time when it matters:
var _ http.Handler = (*Server)(nil).
Middleware works the same way: anything with ServeHTTP is an http.Handler, so handlers wrap handlers.