Skip to content

Go Generics

Generics allow you to write functions and data structures that work with any type, while still keeping the code type-safe.

Basic Generic Function

Instead of writing separate functions for int, float64, and string, you can use a type parameter like [T any].

package main

import "fmt"

// MapKeys takes a map of any type and returns its keys in a slice
func MapKeys[K comparable, V any](m map[K]V) []K {
    r := make([]K, 0, len(m))
    for k := range m {
        r = append(r, k)
    }
    return r
}

func main() {
    var m = map[int]string{1: "2", 2: "4", 4: "8"}

    // Go can often infer the types, so we don't need MapKeys[int, string](m)
    fmt.Println("keys:", MapKeys(m))
}

Generic Structs

You can also create structs that hold any type.

1
2
3
4
5
6
7
8
type List[T any] struct {
    head, tail *element[T]
}

type element[T any] struct {
    next *element[T]
    val  T
}

Type Constraints

Sometimes you don't want "any" type, but only types that support certain operations (like addition).

// Number is a constraint that allows only int and float64
type Number interface {
    int | float64
}

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

Why use Generics?

  1. Reduce Code Duplication: No need to write the same logic for multiple types.
  2. Type Safety: Unlike using any (the empty interface), generics catch errors at compile time.
  3. Cleaner Data Structures: You can build generic stacks, queues, or linked lists that work with any type you put in them.