Introduction
Generics, introduced in Go 1.18, allow you to write functions and data structures that work with different types while maintaining full compile-time type safety. Before generics, you had to choose between code duplication and losing type safety with interface{}. Understanding type parameters is the foundation for writing reusable Go code.
Key Concepts
- Type Parameter: A placeholder type declared in square brackets (e.g.,
[T any]) that gets replaced with a concrete type at compile time. - Constraint: An interface that restricts which types a type parameter can accept.
- Type Inference: The compiler's ability to automatically determine type arguments from the values you pass.
Real World Context
Without generics, writing a Map function that transforms a slice required either writing one version per type (MapInt, MapString) or accepting []interface{} and losing type safety. Generics let you write it once, and the compiler catches type mismatches.
Deep Dive
A generic function declares type parameters in square brackets before the regular parameters:
gofunc Map[T any, R any](s []T, f func(T) R) []R { result := make([]R, len(s)) for i, v := range s { result[i] = f(v) } return result }
The constraint after each type parameter controls what operations you can perform. The three most common built-in constraints are:
any: Alias forinterface{}, allows any type but only basic operations (assignment, comparison with reflect).comparable: Supports==and!=, required for map keys.cmp.Ordered: From the standard librarycmppackage (Go 1.21+), supports ordering operators<,<=,>,>=.
Here is a function using cmp.Ordered:
goimport "cmp" func Min[T cmp.Ordered](x, y T) T { if x < y { return x } return y }
The compiler infers types when possible, so you can write Min(3, 5) instead of Min[int](3, 5).
Common Pitfalls
- Using
anywhen you need ordering — If your function uses<or>, you must constrain withcmp.Ordered, notany. The compiler will reject it. - Importing the deprecated
golang.org/x/exp/constraints— Since Go 1.21, use the standard librarycmppackage instead. Thecmp.Orderedconstraint is built in.
Best Practices
- Let the compiler infer types — Only specify type arguments explicitly when inference fails or when it improves readability.
- Use the most specific constraint —
comparableis better thananywhen you need equality, andcmp.Orderedwhen you need ordering.
Summary
- Type parameters let you write type-safe generic functions and types.
- Constraints (
any,comparable,cmp.Ordered) control what operations are allowed. - The
cmppackage in the standard library providesOrderedand other utilities. - Type inference usually makes explicit type arguments unnecessary.
Code Examples
import "cmp"
// Min returns the smaller of two ordered values.
// cmp.Ordered allows int, float64, string, and other ordered types.
func Min[T cmp.Ordered](x, y T) T {
if x < y {
return x
}
return y
}
// Usage:
// Min(3, 5) // returns 3 (int inferred)
// Min("a", "b") // returns "a" (string inferred)