Generics
Before Go 1.18, writing a function that worked over any type meant either code duplication or falling back to interface{} and losing type safety. Generics fix that.
Type parameters
Section titled “Type parameters”func Map[T, U any](items []T, fn func(T) U) []U { result := make([]U, len(items)) for i, item := range items { result[i] = fn(item) } return result}T and U are type parameters — the compiler infers them from the arguments at each call site, so callers never write Map[int, string](...) explicitly unless inference fails.
Constraints
Section titled “Constraints”any (alias for interface{}) allows literally any type, but you’ll often want to restrict to types that support certain operations:
type Number interface { int | int64 | float64}
func Sum[T Number](items []T) T { var total T for _, item := range items { total += item } return total}The standard library’s constraints-style interfaces live in golang.org/x/exp/constraints, and the built-in cmp.Ordered constraint (from the cmp package) covers anything you can compare with <.
Generic types
Section titled “Generic types”Type parameters aren’t just for functions — types can have them too:
type Stack[T any] struct { items []T}
func (s *Stack[T]) Push(item T) { s.items = append(s.items, item)}
func (s *Stack[T]) Pop() (T, bool) { var zero T if len(s.items) == 0 { return zero, false } item := s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return item, true}When to reach for generics
Section titled “When to reach for generics”Generics shine for containers and algorithms (stacks, sets, Map/Filter/Reduce helpers) that are naturally type-agnostic. For business logic, a plain interface is usually clearer — don’t reach for type parameters just because you can.