Generics let you write data structures and functions with types that are specified later. Functions and types can have type parameters, and instantiated with type arguments, at compile time. Because this occurs at compile time, type parameters — have type constraints. Type constraints restrict the set of allowed type arguments.
Type parameters are enclosed in square brackets, have an identifier and set of constraints. Each named type parameter acts as a place holder that is replaced with a type argument upon instantiation. The type of the type parameter is its type constraint. A type constraint is an interfaces that defines a set of allowed types for the respective parameter.
Without type parameters
First we define max
without type parameters
func max(a, b int) int {
if a > b {
return a
}
return b
}
To call it with int
as argument types
max(int(1), int(2))
And a compile error if we change the argument type to an uint
for instance.
max(uint(1), uint(2))
cannot use uint(1) (constant 1 of type uint) as type int in argument to ma
With type parameters
We can change the definition of max
to use type parameters
func max[T int](a, b T) T {
if a > b {
return a
}
return b
}
And we can call it with an int
. However, the compiler still complains if we
call it with an uint
.
uint does not implement int
Set of type constraints
So we define a set of type constraints that satisfy our requirement.
func max[T int|uint](a, b T) T {
if a > b {
return a
}
return b
}
We can now call max
with an int
and uint
.
max(int(1), int(2))
max(uint(1), uint(2))
Naming type constraints
To re-use constraints it's more convenient to define them as named type constraints
type Integer interface {
int | uint
}
And then you can simply refer to the constraint by the identifier you gave it in your function and type definition.
func max[T Integer](a, b T) T {
if a > b {
return a
}
return b
}
Señor type constraint
In the spec they write "The type set of a term of the form ~T
is the set of
types whose underlying type is T
."
Effectivly, this means to allow a type such as type MyInt int
to be accepted
where the type constraint says int
, you must denote it with a tilde ~int
.
So we update the Integer constraint
type Integer interface {
~int | ~uint
}
To enable us to write
type MyInt int
println(max(MyInt(5), MyInt(1)))
Complete example
Finally, a complete listing of the program
package main
type Integer interface {
~int | ~uint
}
func max[T Integer](a, b T) T {
if a > b {
return a
}
return b
}
func main() {
println(max(int(1), int(2)))
println(max(uint(5), uint(1)))
type MyInt int
println(max(MyInt(5), MyInt(1)))
}
> go run max.go
2
5
5