Type parameters, constraints, and the comparable / any keywords.
Constraints
A type parameter without a constraint isn't very useful — Go won't let you do anything to it because it might be any type. Constraints describe what the type must look like.
Built-in constraints
| Constraint | Means |
| ------------- | ------------------------------------------------- |
| any | Anything. (Same as the empty interface.) |
| comparable | Anything you can compare with == and !=. |
func In[T comparable](needle T, haystack []T) bool {
for _, x := range haystack {
if x == needle { return true }
}
return false
}
Union types: A | B | C
You can list specific types separated by |. Either spelled inline or — usually
nicer — defined as a named interface.
type Number interface {
int | int64 | float64
}
func Sum[T Number](xs []T) T {
var sum T
for _, x := range xs { sum += x }
return sum
}
Approximation: ~T
~int means "any type whose underlying type is int". That includes named
types like type Celsius int.
type Integer interface {
~int | ~int32 | ~int64
}
Almost always use the tilde form for numeric constraints — otherwise your
function rejects type MyInt int.
cmp.Ordered (Go 1.21+)
The standard library ships a cmp package with the most common shape:
package cmp
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ... |
~float32 | ~float64 |
~string
}
Use cmp.Ordered for "anything I can compare with < and >". That's how
slices.Sort is generic. Before Go 1.21, golang.org/x/exp/constraints provided
the same thing.