Constraints specify what types can be used as type parameters. They're crucial for writing useful generic code.
Without constraints, you can only use operations available to any (which is almost nothing). Constraints allow you to specify what operations are available, making generic code more useful.
Built-in constraints
// any: any type (equivalent to interface{})
func Print[T any](v T) { }
// comparable: types that can be compared with == and !=
func Find[T comparable](slice []T, value T) int {
for i, v := range slice {
if v == value {
return i
}
}
return -1
}
Union constraints
Specify multiple types with |:
// T can be int, int64, or float64
func Add[T int | int64 | float64](a, b T) T {
return a + b
}
Interface constraints
Use interfaces as constraints to specify required methods:
type Stringer interface {
String() string
}
func PrintString[T Stringer](v T) {
fmt.Println(v.String())
}
Interface constraints are powerful because they allow you to specify behavior requirements:
// Constraint requires types with Add method
type Adder[T any] interface {
Add(other T) T
}
func Sum[T Adder[T]](items []T) T {
var sum T
for _, item := range items {
sum = sum.Add(item)
}
return sum
}
Type sets (Go 1.18+)
You can define custom constraints using type sets:
// Numeric constraint
type Numeric interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
func Multiply[T Numeric](a, b T) T {
return a * b
}
Approximate constraints (Go 1.18+)
Use ~ to allow underlying types:
type MyInt int
// Without ~, MyInt can't be used
func Process[T int](v T) { }
// With ~, MyInt can be used (underlying type is int)
func Process[T ~int](v T) { }
func main() {
var x MyInt = 42
Process(x) // OK with ~int, error with int
}
Approximate constraints matter because they allow custom types based on built-in types to work with generics:
type ID int
type Count int
// Without ~, you'd need separate functions for ID and Count
// With ~, one function works for both
func Increment[T ~int](v T) T {
return v + 1
}