| name | golang-types-and-interfaces |
| description | Go type system — structs, interfaces, embedding, composition, generics, slices, maps, enums with iota. TRIGGER when: user asks about Go structs, Go interfaces, Go embedding, composition in Go, Go generics, type parameters, Go type constraints, Go slices, Go maps, Go iota, Go enum, Go bitmask, implicit interface, interface satisfaction, small interfaces, accept interfaces return structs, Go type assertion, Go type switch, when to use generics in Go, Go collections, Go struct tags, Go composition vs inheritance, Go polymorphism. DO NOT USE when: user asks about OOP design principles or composition/polymorphism in a language-agnostic way with no Go code involved — use `oop-principles` instead.
|
| user-invocable | false |
Go Types and Interfaces
Go has no classes, no inheritance, no implements keyword. It uses composition, small interfaces, and implicit satisfaction to achieve polymorphism.
Interface Design Rules
| Rule | Rationale |
|---|
| Accept interfaces, return structs | Callers define the contract they need; implementations stay concrete |
| Keep interfaces small (1-3 methods) | io.Reader is the gold standard — one method, universally useful |
| Define interfaces at the consumer site | The package that uses the interface defines it, not the package that implements it |
Implicit satisfaction — no implements | Types satisfy interfaces by having the right methods, no declaration needed |
| Don't export interfaces from implementation packages | Let consumers define what they need |
type UserStore interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type PostgresStore struct { db *sql.DB }
func (s *PostgresStore) FindByID(ctx context.Context, id string) (*User, error) {
}
Composition Decision Table
| Need | Go pattern | Not this |
|---|
| Reuse behavior | Struct embedding | Inheritance |
| Polymorphism | Interfaces | Abstract base class |
| Has-a relationship | Regular field | Embedding |
| Extend interface contract | Interface embedding | Interface inheritance |
| Share code across types | Package-level functions | Base class methods |
type Server struct {
http.Server
logger *slog.Logger
}
type ReadCloser interface {
Reader
Closer
}
Generics Quick Guide (Go 1.18+)
| Use generics when | Don't use generics when |
|---|
| Writing containers (stack, queue, set) | An interface already solves it |
| Algorithms over any ordered/comparable type | It adds complexity without reducing duplication |
| Reducing duplication across type-safe functions | Only 1-2 concrete types exist |
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
names := Map(users, func(u User) string { return u.Name })
See Collections & Generics for type constraints (~int | ~float64, comparable) and the slices/maps/cmp packages.
Enums with Iota
Go has no enum keyword. Use typed constants with iota (sequential), or 1 << iota for bitmask flags. Reserve the zero value for "unknown" to catch uninitialized values, and give the type a String() method.
See Collections & Generics for full enum, bitmask, and stringer examples.
Anti-patterns
| Anti-pattern | Problem | Fix |
|---|
| Interface pollution (10+ methods) | Weak abstraction, hard to implement/mock | See Interface Design Rules — keep interfaces small (1-3 methods) |
| Premature interfaces | Interface defined before second implementation exists | Wait until you need polymorphism |
| Embedding for code reuse without is-a | Promoted methods leak into API surface | Use a regular field instead |
any / interface{} everywhere | Erases type safety | Use specific interfaces or generics |
| Generics for 1-2 concrete types | Over-engineering | See Generics Quick Guide — write the concrete functions |
Read On Demand