---
slug: generics-in-go-1.18
title: "Generics in Go 1.18"
date: "2022-01-17T00:49:28-04:00"
---
I spent some time trying the [generics types and generic
functions][generics] 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.

```go
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][]:

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

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

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

```go
// 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][go-docs] as of this writing) that provides a
couple of useful unions, but it's relatively anemic at the moment:

```bash
$ 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.
```
&nbsp;

Using the `constraints` package, the `Frac` type from the previous
example could be written like this:

```go
import "constraints"

// Fraction
type Frac[T constraints.Signed] struct {
  num T // numerator
  den T // denominator
}
```
&nbsp;

And with `constraints`, the `Sum()` function from the first example
could be defined like this:

```go
// 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
}
```
&nbsp;

Other useful tidbits:

* No [type erasure][].
* The standard library is still backwards compatible, so there is no
  need to rewrite your existing code.
* There are [two new tutorials][] which explain generics and fuzzing.
* [Type constraints section][] of [Go spec][].

**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][constraints-exp].  [LWN][] has an [excellent
summary of Go 1.18][lwn-go-1.18].

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

[go]: https://go.dev/
  "Go programming language"
[go 1.18]: https://tip.golang.org/doc/go1.18
  "Go 1.18"
[go 1.17]: https://go.dev/doc/go1.17
  "Go 1.17"
[generics]: https://en.wikipedia.org/wiki/Generic_programming
  "Generic programming"
[type inference]: https://en.wikipedia.org/wiki/Type_inference
  "Automatic detection of variable types."
[go-docs]: https://pkg.go.dev/
  "Online Go package documentation."
[type erasure]: https://en.wikipedia.org/wiki/Type_erasure
  "Type erasure."
[two new tutorials]: https://go.dev/blog/tutorials-go1.18
  "New Go tutorials which explain generics and fuzzing."
[type constraints section]: https://tip.golang.org/ref/spec#Type_constraints
  "Type constraints section of Go specification."
[go spec]: https://tip.golang.org/ref/spec
  "Go language specification."
[constraints-exp]: https://github.com/golang/go/issues/50792
  "constraints: move to x/exp for Go 1.18"
[lwn]: https://lwn.net/
  "Linux Weekly News"
[lwn-go-1.18]: https://lwn.net/Articles/883602/
  "What's coming in Go 1.18"
[go 1.18 released]: https://go.dev/blog/go1.18
  "Go 1.18 release announcement."