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