Go 1.18 added type parameters to function and type declarations. A generic declaration is instantiated by substituting type arguments for its type parameters. Later releases added generic aliases (Go 1.24), self-reference in type parameter lists (Go 1.26) and generic methods (Go 1.27).
Type parameter lists
TypeParameters = "[" TypeParamList [ "," ] "]" .
TypeParamList = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl = IdentifierList TypeConstraint .
The list looks like an ordinary parameter list in square brackets, except that the names must all be present (spec: Type parameter declarations). Each name declares a type parameter, a new and different named type that stands for an unknown type. Non-blank names must be unique.
[P any]
[S interface{ ~[]byte | string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]
func Map[T, U any](s []T, f func(T) U) []U {
r := make([]U, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
type List[T any] struct {
next *List[T]
value T
}
func (l *List[T]) Len() int { /* … */ return 0 }
A parsing ambiguity arises when a generic type has a single type parameter P
with a constraint C such that P C forms a valid expression. It is
then parsed as an array type. To resolve it, embed the constraint in an interface or use a
trailing comma:
type T[P interface{*C}] … // constraint is the interface
type T[P *C,] … // trailing comma
Since Go 1.26 a generic type may refer to itself in its own type parameter list, so a constraint can require a type like itself.
type Adder[A Adder[A]] interface {
Add(A) A
}
func algo[A Adder[A]](x, y A) A {
return x.Add(y)
}
Type constraints
TypeConstraint = TypeElem .
A type constraint is an interface that defines the type arguments allowed for a
type parameter and controls which operations its values support (spec: Type constraints). If
the constraint is an interface literal of the form interface{E} with
E a type element, not a method, the enclosing interface{ … } may be
omitted in a type parameter list:
[T []P] // = [T interface{[]P}]
[T ~int] // = [T interface{~int}]
[T int | string] // = [T interface{int|string}]
type Constraint ~int // illegal: ~int is not in a type parameter list
Unions, tilde terms and method elements are described on the
Interfaces page. The standard library’s
cmp.Ordered covers the ordered types.
comparable
The predeclared interface comparable (Go 1.18) denotes all non-interface
types that are strictly comparable. It
may only be used as a constraint, as may interfaces that embed it.
Satisfying a constraint
A type argument T satisfies a constraint C if T
implements C. As an exception (Go 1.20), C may be written as
interface{ comparable; E } with E a basic interface, and
T is comparable, though not necessarily strictly, and implements
E. Because of this exception, comparing values of a type parameter may panic
at run time.
// type argument constraint result
// int interface{ ~int } satisfied: int implements it
// string comparable satisfied: string is strictly comparable
// []byte comparable not satisfied: slices are not comparable
// any comparable satisfied: any is comparable
// any interface{ comparable; int } not satisfied: any does not implement int
Instantiation
Instantiation substitutes each type argument for its type parameter across the declaration, then checks that each type argument satisfies its (possibly instantiated) constraint (spec: Instantiations). Instantiating a type gives a new non-generic named type; instantiating a function gives a new non-generic function. A generic type must be instantiated with all its type arguments given explicitly.
func sum[T ~int | ~float64 | ~string](x ...T) T { /* … */ }
x := sum // illegal: the type of x is unknown
intSum := sum[int] // intSum has type func(x ...int) int
a := intSum(2, 3) // a has value 5 of type int
b := sum[float64](2.0, 3) // b has value 5.0 of type float64
c := sum(b, -1) // c has value 4.0 of type float64
type sumFunc func(x ...string) string
var f sumFunc = sum // same as var f sumFunc = sum[string]
A list of type arguments may be partial; the missing ones, always on the right, are inferred. A partial list cannot be empty.
Type inference
A use of a generic function may omit type arguments if they can be inferred from the context (spec: Type inference). Inference solves a set of type equations built from pairs of matched types, such as an argument and its parameter, or a type argument and its constraint. Type argument lists may be omitted entirely if the function is called with ordinary arguments, assigned to a variable with a known type, passed as an argument to another function, or returned as a result.
- Go 1.21 extended inference to generic functions passed as arguments or assigned, and to methods of interface and constraint types.
- Since Go 1.21, untyped constants of different kinds passed for one type parameter resolve as for operators, by the same rule used for constant expressions.
- Since Go 1.27, inference also applies in any context where a generic function is assigned to, or converted to, a non-generic function type.
// dedup returns a copy of the argument slice with any duplicate entries removed.
func dedup[S ~[]E, E comparable](S) S { /* … */ return nil }
type Slice []int
var s Slice
s = dedup(s) // same as s = dedup[Slice, int](s)
Generic declarations
- Generic types must be instantiated when used. The given type in a type
definition may not be a type parameter:
type T[P any] Pis illegal. - Generic aliases (Go 1.24) must be instantiated when used; see Alias declarations.
- Function literals cannot declare type parameters.
- Interface methods cannot declare type parameters.
Receiver type parameters
A method of a generic type declares corresponding type parameters in its receiver, one for each type parameter of the base type. They look like an instantiation of the base type, and the names need not match the declaration. Their constraints are implied by the base type.
type Pair[A, B any] struct {
a A
b B
}
func (p Pair[A, B]) Swap() Pair[B, A] { /* … */ } // receiver declares A, B
func (p Pair[First, _]) Fst() First { /* … */ } // receiver declares First for A
A receiver type written through an alias must not be generic and must not denote an instantiated type.
Generic methods
Since Go 1.27 a method declaration may declare its own type parameters, in addition to any declared by the receiver, and the method name denotes a generic method that must be instantiated before it is called or used as a value (spec: Method declarations). Methods of interfaces may not declare type parameters, and an interface method cannot be implemented by a generic method.
type List[E any] []E
// Apply returns the list obtained from applying f to each element of l.
func (l List[E]) Apply[F any](f func(E) F) List[F] {
r := make(List[F], len(l))
for i, x := range l {
r[i] = f(x)
}
return r
}
Operations on type parameters
An operation on a value of type parameter type must be valid for every type in the type set of its constraint. Calls of methods work as for interfaces.
- Operators must apply to each type in the set. They are computed with the
precision of the type argument: in
func dotProduct[F ~float32|~float64](v1, v2 []F) F, arithmetic is done atfloat32orfloat64precision depending onF. - Indexing requires identical element types across the set, with a string
counted as
byte. If the set includes a map, all types must be maps with identical keys. If the set includes strings, the result cannot be assigned to. - Conversions must be valid for each type in the set. Converting a constant to a type parameter gives a non-constant value.
- Struct fields of a type parameter value cannot be accessed.
- Embedding. A struct cannot embed a type parameter, or a pointer to one.
- Complex numbers.
real,imagandcomplexdo not accept arguments of type parameter type. - A type switch needs an interface value: convert first, as in
switch any(x).(type).