Go back to overview

of type constraints

in Go

April 23, 2022

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.

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

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

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))
```

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
}
```

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)))
```

Finally, a complete listing of the program

max.go

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
```