Generics (type parameters)
Write one function that works for many types, checked at compile time.
Go 1.18 added type parameters and constraints β generic Map/Filter/Reduce, comparable lookups, and reusable containers, all statically type-safe with zero reflection.
Type parameters and constraints
A type parameter is a placeholder for a type that the caller (or the compiler) fills in. You declare it in square brackets after the function name: func Max[T cmp.Ordered](a, b T) T. The cmp.Ordered part is a constraint β an interface that restricts which types T may be. Here it limits T to types that support <, >, and the other ordering operators.
Constraints are just interfaces, but generics let an interface list type sets (with the ~ and | syntax) in addition to methods. ~int means "any type whose underlying type is int", so your own type Celsius float64 still satisfies a ~float64 constraint. The blank constraint any (an alias for interface{}) imposes no restriction at all.
comparable and inference
The predeclared constraint comparable matches every type usable with == and != β exactly the types that can be a map key. Use it for membership tests (Contains), set keys, and de-duplication. It is strictly narrower than any: slices, maps, and funcs are not comparable and won't satisfy it.
You rarely write the type arguments by hand. Go infers them from the values you pass: Max(3, 7) deduces T = int, and Map(nums, strconv.Itoa) deduces both T and U from the slice and the function. You only spell them out (Max[float64](a, b)) when inference can't.
Generic data structures
Type parameters also go on types. type Stack[T any] struct { items []T } is one definition that yields a Stack[int], a Stack[string], or a Stack[User] β each fully type-checked, with no interface{} boxing and no runtime casts. Methods on a generic type reuse the receiver's parameter (func (s *Stack[T]) Push(v T)); they cannot introduce new type parameters of their own.
Try it 5 examples
Generic Max with an ordered constraint
GointroOne Max body serves int, float64, and string β the compiler picks T from each call, so "apple" vs "pear" is decided by lexicographic byte order (pear wins). The cmp.Ordered constraint is what permits the > comparison; an unconstrained any would not compile.