What is a constraint in Go generics? Show the different ways to write one, including the [P *C] parsing gotcha.
Question 114MediumGo 1.22 to 1.25
A constraint is an interface that limits which type arguments are allowed. Since Go 1.18 an interface defines a type set. It can list methods, type terms (int | string), approximation terms (~int), or a mix of these.
type Number interface {
~int | ~int64 | ~float64
}
func Sum[T Number](xs []T) T { var s T; for _, x := range xs { s += x }; return s }
// Inline constraint; interface{ ... } may be omitted around a single union
func Abs[T ~int | ~float64](x T) T { if x < 0 { return -x }; return x }
// Methods + type terms together
type StringerInt interface {
~int
String() string
}
Gotcha: in a generic type declaration, type T[P *C] struct{} is ambiguous. The parser can read it as an array length expression P * C. To fix it, wrap the constraint in interface{} or add a trailing comma:
type Ptr[P interface{ *int }] struct{ p P } // OK
type Ptr2[P *int,] struct{ p P } // OK: trailing comma
any is an alias for interface{}. It is the most permissive constraint, and with it only operations valid for all types are allowed: assignment, passing the value, and conversion to any.
More on Generics
- Q112What problem do type parameters solve that
interface{}/anyparameters did not? - Q113What is the difference between
func F(s fmt.Stringer)andfunc F[T fmt.Stringer](s T)? When does it matter? - Q115What does the
~(tilde) token mean in a constraint? What happens without it? - Q116Explain type sets. How do unions and intersections combine, and which interfaces can only be used as constraints?
- Q117What is
comparable? What changed in Go 1.20, and what does this program print? - Q118Which operations can you perform on a value of type parameter type? Why can't you access a struct field even when every type in the set has it?