Package constraints provides a set of useful constraint interfaces to be used with Go generic type parameters. These constraints enable generic functions to work with specific categories of types, such as signed integers, floating-point numbers, or any ordered types.
go get golang.org/x/exp/constraintsimport (
"golang.org/x/exp/constraints"
)Alternative import pattern for specific constraints:
import (
"golang.org/x/exp/constraints"
)
// Use in type parameters
func Max[T constraints.Ordered](a, b T) T {
if a < b {
return b
}
return a
}package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
// Generic function that works with any signed integer
func AddSigned[T constraints.Signed](a, b T) T {
return a + b
}
// Generic function that works with any floating-point type
func MaxFloat[T constraints.Float](a, b T) T {
if a > b {
return a
}
return b
}
// Generic function that works with any ordered type
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func main() {
// Signed integers
result1 := AddSigned(int32(5), int32(3))
fmt.Println(result1) // 8
// Floating-point types
result2 := MaxFloat(3.14, 2.71)
fmt.Println(result2) // 3.14
// Ordered types (string)
result3 := Min("apple", "banana")
fmt.Println(result3) // apple
}Permits any signed integer type. Useful for generic functions that need to work with signed integers of any size.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}The Signed constraint permits any signed integer type. If future releases of Go add new predeclared signed integer types, this constraint will be modified to include them.
Use case: Generic functions performing arithmetic operations or comparisons on signed integer values.
Example:
func Sum[T constraints.Signed](values ...T) T {
var total T
for _, v := range values {
total += v
}
return total
}
// Works with any signed integer type
sum := Sum(int64(1), int64(2), int64(3))Permits any unsigned integer type. Useful for generic functions working with unsigned integers or bitwise operations.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}The Unsigned constraint permits any unsigned integer type. If future releases of Go add new predeclared unsigned integer types, this constraint will be modified to include them.
Use case: Generic functions handling unsigned values, memory offsets, or bit manipulation.
Example:
func IsPowerOfTwo[T constraints.Unsigned](n T) bool {
return n > 0 && (n&(n-1)) == 0
}
// Works with any unsigned integer type
isPower := IsPowerOfTwo(uint32(16))Permits any integer type (both signed and unsigned). This is a composite constraint combining Signed and Unsigned.
type Integer interface {
Signed | Unsigned
}The Integer constraint permits any integer type. If future releases of Go add new predeclared integer types, this constraint will be modified to include them.
Use case: Generic functions that need to work with any integer type regardless of signedness.
Example:
func Abs[T constraints.Integer](n T) T {
if n < 0 {
return -n
}
return n
}
// Works with both signed and unsigned integers
absValue := Abs(int32(-42))Permits any floating-point type. Useful for generic functions performing mathematical operations on float types.
type Float interface {
~float32 | ~float64
}The Float constraint permits any floating-point type. If future releases of Go add new predeclared floating-point types, this constraint will be modified to include them.
Use case: Generic mathematical functions, statistical calculations, or scientific computations.
Example:
func Average[T constraints.Float](values ...T) T {
var sum T
for _, v := range values {
sum += v
}
return sum / T(len(values))
}
// Works with any floating-point type
avg := Average(3.14, 2.71, 1.41)Permits any complex numeric type. Useful for generic functions working with complex numbers.
type Complex interface {
~complex64 | ~complex128
}The Complex constraint permits any complex numeric type. If future releases of Go add new predeclared complex numeric types, this constraint will be modified to include them.
Use case: Generic functions for complex number arithmetic, signal processing, or advanced mathematical operations.
Example:
func Magnitude[T constraints.Complex](c T) float64 {
r := real(c)
i := imag(c)
return math.Sqrt(r*r + i*i)
}
// Works with any complex type
mag := Magnitude(complex(3, 4))Permits any ordered type - any type that supports the comparison operators (<, <=, >=, >). This constraint enables sorting and minimum/maximum operations on any comparable ordered type.
type Ordered = cmp.OrderedThe Ordered constraint permits any ordered type. If future releases of Go add new ordered types, this constraint will be modified to include them.
Note: This type is redundant since Go 1.21 introduced cmp.Ordered. For Go 1.21+, prefer using cmp.Ordered directly.
Use case: Generic functions for sorting, finding minimum/maximum values, or other operations requiring ordering.
Example:
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
// Works with strings, numbers, or any comparable type
maxStr := Max("apple", "zebra")
maxInt := Max(10, 20)
minFloat := Min(3.14, 2.71)The constraints in this package form the following hierarchy:
package constraints // import "golang.org/x/exp/constraints"
// Signed is a constraint that permits any signed integer type.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
// Unsigned is a constraint that permits any unsigned integer type.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Integer is a constraint that permits any integer type (signed or unsigned).
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
type Float interface {
~float32 | ~float64
}
// Complex is a constraint that permits any complex numeric type.
type Complex interface {
~complex64 | ~complex128
}
// Ordered is a constraint that permits any ordered type.
// This is equivalent to cmp.Ordered and is redundant in Go 1.21+.
type Ordered = cmp.Ordered// Clamp constrains a value to a range
func Clamp[T constraints.Ordered](value, min, max T) T {
if value < min {
return min
}
if value > max {
return max
}
return value
}
// Usage
clampedInt := Clamp(5, 1, 10) // 5
clampedStr := Clamp("b", "a", "c") // "b"// Some types might satisfy multiple constraints
// Use union syntax for functions needing specific types
// Function for numeric types that can be ordered
func LinearInterpolate[T constraints.Float](a, b, t T) T {
return a*(1-t) + b*t
}
// Function for any comparable type
func Contains[T constraints.Ordered](slice []T, value T) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
}// Calculate statistical measures
func Variance[T constraints.Float](values []T) T {
if len(values) == 0 {
return 0
}
var mean T
for _, v := range values {
mean += v
}
mean /= T(len(values))
var variance T
for _, v := range values {
diff := v - mean
variance += diff * diff
}
variance /= T(len(values))
return variance
}
// Calculate magnitude of complex numbers
func Conjugate[T constraints.Complex](c T) T {
return T(complex(real(c), -imag(c)))
}Instead of using type switches, you can use constraints to handle multiple types generically:
// Without constraints (type switch)
func PrintValue(v interface{}) {
switch val := v.(type) {
case int:
fmt.Println("int:", val)
case float64:
fmt.Println("float:", val)
case string:
fmt.Println("string:", val)
}
}
// With constraints (generic)
func PrintValue[T constraints.Ordered](v T) {
fmt.Println("value:", v)
}// Define custom constraint combinations as needed
type Numeric interface {
constraints.Integer | constraints.Float
}
func Square[T Numeric](value T) T {
return value * value
}
// Works with any integer or float type
sqInt := Square(int32(5)) // 25
sqFloat := Square(3.5) // 12.25~ in constraint definitions allows named types whose underlying type matches. For example, a custom type MyInt with underlying type int satisfies the Signed constraint.Ordered constraint is redundant in Go 1.21 and later. Use cmp.Ordered from the standard library instead for new code.