08 — Generics

Writing one function that works for many types without losing type safety: type parameters and constraints. Every example is live — edit it and press Run.

Type parameters and constraints

A type parameter is a placeholder type in square brackets after the function name. The compiler fills it in per call (usually inferred, so you call it like an ordinary function) and type-checks each use. A constraint says which operations are allowed:

package main

import (
    "cmp"
    "fmt"
    "strconv"
)

func Map[T, U any](in []T, f func(T) U) []U {
    out := make([]U, len(in))
    for i, x := range in {
        out[i] = f(x)
    }
    return out
}

func Filter[T any](in []T, keep func(T) bool) []T {
    var out []T
    for _, x := range in {
        if keep(x) {
            out = append(out, x)
        }
    }
    return out
}

func Max[T cmp.Ordered](xs []T) (T, bool) {
    if len(xs) == 0 {
        var zero T
        return zero, false
    }
    m := xs[0]
    for _, x := range xs[1:] {
        if x > m {           // allowed because T is cmp.Ordered
            m = x
        }
    }
    return m, true
}

func main() {
    fmt.Println(Map([]int{1, 2, 3}, func(x int) int { return x * 2 }))
    fmt.Println(Map([]int{1, 2, 3}, strconv.Itoa))   // T=int, U=string
    fmt.Println(Filter([]int{1, 2, 3, 4, 5, 6}, func(x int) bool { return x%2 == 0 }))
    fmt.Println(Max([]int{3, 7, 2, 9, 4}))
    fmt.Println(Max([]string{"apple", "cherry", "banana"}))
}

Output:

[2 4 6]
[1 2 3]
[2 4 6]
9 true
cherry true

One Max works for []int, []string, []float64 — one implementation, checked per type. var zero T gives the zero value for whatever T turns out to be. Go generics compile to code on the real types (closer to C++ templates than to a void * container), so there's no boxing in the common case.

Recap

ConceptIn Go
Type parameterfunc F[T any](...)
CallingType args inferred: Map(xs, f)
anyAny type; move values only
comparablePermits == / !=
cmp.OrderedPermits <, > (numbers, strings)

Next: 09 — io.Reader & io.Writer.