thecodex.expert · The Codex Family of Knowledge
Go

Generics

For its first nine years, Go had no generics at all — they finally arrived in Go 1.18 (March 2022) after years of public design debate.

Go 1.26 Added in Go 1.18 (2022) Last verified:
Canonical Definition

Generics let a function or type declare one or more type parameters in square brackets, each constrained by an interface that specifies which types are allowed. Before Go 1.18 (released March 2022), writing a function that worked across multiple types meant either duplicating code per type or falling back to interface{} with runtime type assertions; generics give compile-time type safety instead, without sacrificing Go’s single-implementation style.

A generic function

A type parameter list in square brackets follows the function name, naming a placeholder type and its constraint. The built-in constraint any (an alias for interface{}) allows every type; numeric constraints restrict to types that support operators like <.

Gogenerics.go
func Max[T int | float64](a, b T) T {
    if a > b {
        return a
    }
    return b
}

fmt.Println(Max(3, 7))         // 7 — T inferred as int
fmt.Println(Max(2.5, 1.1))     // 2.5 — T inferred as float64

Constraints: which types are allowed

A constraint is just an interface, optionally listing specific allowed types with |. The standard library's constraints package (and the built-in comparable constraint, for types usable with ==) provide common ones, and you can declare your own.

Gocustom_constraint.go
type Number interface {
    int | int64 | float32 | float64
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

fmt.Println(Sum([]int{1, 2, 3}))          // 6
fmt.Println(Sum([]float64{1.5, 2.5}))     // 4.0

Generic types, not just functions

Types can also take type parameters, which is how you'd write a single, reusable, type-safe container (a stack, a linked list, a set) instead of one implementation per type or falling back to interface{}.

Gogeneric_type.go
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
    var zero T
    if len(s.items) == 0 {
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

var s Stack[string]   // a Stack that only ever holds strings
s.Push("a")

Sources

1
The Go Authors. "Tutorial: Getting started with generics", go.dev/doc/tutorial/generics, and "An Introduction to Generics", go.dev/blog/intro-generics.