I spent some time trying the generics types and generic
functions in Go 1.18, and I like what they’ve got so far.

Below is a Go 1.17 program which prints the results of the following
functions:

`SumInts()`

: calculate the the sum of of a slice of `int`

values.`SumFloats()`

: calculate the the sum of of a slice of `float32`

values.`SumComplexes()`

: calculate the the sum of of a slice of `complex64`

values.

```
package main
import "fmt"
// Return sum of all values in slice of ints.
func SumInts(m []int) int {
var r int
for _, v := range(m) {
r += v
}
return r
}
// Return sum of all values in slice of float32s.
func SumFloats(m []float32) float32 {
var r float32
for _, v := range(m) {
r += v
}
return r
}
// Return sum of all values in slice of complex64s.
func SumComplexes(m []complex64) complex64 {
var r complex64
for _, v := range(m) {
r += v
}
return r
}
var (
// test integers
ints = []int { 10, 20, 30 }
// test floating point numbers
floats = []float32 { 10.0, 20.0, 30.0 }
// test complex numbers
complexes = []complex64 { complex(10, 1), complex(20, 2), complex(30, 3) }
)
func main() {
// print sums
fmt.Printf("ints = %d\n", SumInts(ints))
fmt.Printf("floats = %2.1f\n", SumFloats(floats))
fmt.Printf("complexes = %g\n", SumComplexes(complexes))
}
```

Here’s the same program, written using Go 1.18 generics:

```
package main
import "fmt"
// Return sum of all numeric values in slice.
func Sum[V ~int|~float32|~complex64](vals []V) V {
var r V
for _, v := range(vals) {
r += v
}
return r
}
var (
// test integers
ints = []int { 10, 20, 30 }
// test floating point numbers
floats = []float32 { 10.0, 20.0, 30.0 }
// test complex numbers
complexes = []complex64 { complex(10, 1), complex(20, 2), complex(30, 3) }
)
func main() {
// print sums using generics w/explicit types
fmt.Printf("ints = %d\n", Sum[int](ints))
fmt.Printf("floats = %2.1f\n", Sum[float32](floats))
fmt.Printf("complexes = %g\n", Sum[complex64](complexes))
}
```

You can use type inference to drop the type parameters in many
instances. For example, we can rewrite `main()`

from the previous
example like this:

```
func main() {
// print sums using generics w/explicit types
fmt.Printf("ints = %d\n", Sum(ints))
fmt.Printf("floats = %2.1f\n", Sum(floats))
fmt.Printf("complexes = %g\n", Sum(complexes))
}
```

Generics can also be used in type definitions. Example:

```
package main
import "fmt"
// Fraction
type Frac[T ~int|~int32|~int64] struct {
num T // numerator
den T // denominator
}
// Add two fractions.
func (a Frac[T]) Add(b Frac[T]) Frac[T] {
return Frac[T] { a.num + b.num, a.den * b.den }
}
// Multiple fractions.
func (a Frac[T]) Mul(b Frac[T]) Frac[T] {
return Frac[T] { a.num * b.num, a.den * b.den }
}
// Return inverse of fraction.
func (a Frac[T]) Inverse() Frac[T] {
return Frac[T] { a.den, a.num }
}
// Return string representation of fraction.
func (a Frac[T]) String() string {
return fmt.Sprintf("%d/%d", a.num, a.den)
}
func main() {
// test fractions
fracs = []Frac[int] {
Frac[int] { 1, 2 },
Frac[int] { 3, 4 },
Frac[int] { 5, 6 },
}
// print fractions
for _, f := range(fracs) {
fmt.Printf("%s => %s\n", f, f.Mul(f.Add(f.Inverse())))
}
}
```

Interface type declarations can now be used to define the constraints
that a matching type must satisfy. In addition to the ability to
specify methods that a matching type must implement, a type constraint
specified as an interface may also specify a union of terms indicating
the set of matching types.

Type union terms can be tilde-prefixed (example: `~int`

), which
indicates that the *underlying type* must match the given type.

For example, the `Frac`

type declaration from the previous example could
be written like this instead:

```
// Integral number type.
type integral interface {
~int | ~int32 | ~int64
}
// Fraction
type Frac[T integral] struct {
num T // numerator
den T // denominator
}
```

There are two new predeclared identifiers:

`any`

: An alias for `interface {}`

.`comparable`

: Any type which can be compared for equality with `==`

and `!=`

. Useful for the parameterizing map keys.

There is a new `constraints`

package, which (not yet visible in the
online Go documentation as of this writing) that provides a
couple of useful unions, but it’s relatively anemic at the moment:

```
$ go1.18beta1 doc -all constraints
package constraints // import "constraints"
Package constraints defines a set of useful constraints to be used with type
parameters.
TYPES
type Complex interface {
~complex64 | ~complex128
}
Complex is a constraint that permits any complex numeric type. If future
releases of Go add new predeclared complex numeric types, this constraint
will be modified to include them.
type Float interface {
~float32 | ~float64
}
Float is a constraint that permits any floating-point type. If future
releases of Go add new predeclared floating-point types, this constraint
will be modified to include them.
type Integer interface {
Signed | Unsigned
}
Integer is a constraint that permits any integer type. If future releases of
Go add new predeclared integer types, this constraint will be modified to
include them.
type Ordered interface {
Integer | Float | ~string
}
Ordered is a constraint that permits any ordered type: any type that
supports the operators < <= >= >. If future releases of Go add new ordered
types, this constraint will be modified to include them.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
Signed is a constraint that permits any signed integer type. If future
releases of Go add new predeclared signed integer types, this constraint
will be modified to include them.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
Unsigned is a constraint that permits any unsigned integer type. If future
releases of Go add new predeclared unsigned integer types, this constraint
will be modified to include them.
```

Using the `constraints`

package, the `Frac`

type from the previous
example could be written like this:

```
import "constraints"
// Fraction
type Frac[T constraints.Signed] struct {
num T // numerator
den T // denominator
}
```

And with `constraints`

, the `Sum()`

function from the first example
could be defined like this:

```
// Numeric value.
type Number interface {
constraints.Integer | constraints.Float | constraints.Complex
}
// Return sum of all numeric values in slice.
func Sum[V Number](vals []V) V {
var r V
for _, v := range(vals) {
r += v
}
return r
}
```

Other useful tidbits:

**Update (2021-01-19):** Minor wording changes, add information about
tilde prefixes in type constraints.

**Update (2021-02-13):** The `constraints`

package was moved to
`x/exp`

for Go 1.18. LWN has an excellent
summary of Go 1.18.

**Update (2021-03-17):** Go 1.18 released.