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.

Go specifically avoided supporting generics to keep the language simple. But later the team added support of generics in golang.

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))
}

The hardest part to understand from this code is MapKeys[K comparable, V any]. Here, the keyword K comparable act as strict implementation for a map key. It should support bit operation like equal to, etc. V any is the value of the map and it can be anything. including a interface{}.

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.