GGistDev

Generics in Go

Generics add type parameters to functions and types for reusable, type-safe code.

Basics

Type parameters are declared in brackets. Use any for unconstrained parameters; constraints restrict allowed operations.

package main

func Identity[T any](x T) T { return x }

Constraints

// Built-in: any, comparable
// Custom constraint
type Number interface { ~int | ~int64 | ~float64 }

func Add[T Number](a, b T) T { return a + b }

Notes:

  • The ~ (tilde) allows underlying types to match (e.g., type MyInt int satisfies ~int)
  • comparable permits ==/!=; you cannot constrain to methods—use interfaces on methods for that

Generic helpers

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

func Filter[T any](in []T, pred func(T) bool) []T {
    out := make([]T, 0, len(in))
    for _, v := range in { if pred(v) { out = append(out, v) } }
    return out
}

Generic types

type Stack[T any] struct { data []T }

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

Design: keep generic types small and focused; avoid exposing concrete internals.

Comparable

func IndexOf[T comparable](xs []T, target T) int {
    for i, v := range xs { if v == target { return i } }
        return -1
}

Example usage

nums := []int{1,2,3}
doubles := Map(nums, func(n int) int { return n*2 })
ix := IndexOf(doubles, 4) // 1

Best practices

  • Keep constraints minimal; prefer any unless you truly need operations
  • Avoid over-generalization; start concrete, then generalize
  • Use descriptive type parameter names (T, K, V, In, Out) where helpful
  • Prefer readability over clever type gymnastics
  • Benchmark; sometimes specialized concrete versions outperform generic ones in hot paths

Summary

  • Type parameters + constraints enable reusable, safe abstractions
  • Generic funcs and types integrate naturally with Go’s simplicity