Kinoko's TIL Log

Go Generics

The Point

Generics (Go 1.18+) let you write type-parameterized functions – the type is determined at call time, but type checking happens at compile time. This gives you both flexibility and safety.

Explanation

Without Generics vs with Generics

 1// Without generics: same logic duplicated for each type
 2func sumInts(nums []int) int {
 3    var total int
 4    for _, n := range nums { total += n }
 5    return total
 6}
 7func sumFloats(nums []float64) float64 {
 8    var total float64
 9    for _, n := range nums { total += n }
10    return total
11}
12
13// Or use any, but lose type safety -- errors only show up at runtime
14func sumAny(nums []any) any { ... }
15
16// With generics: one function, compile-time type safety
17func sum[T int | float64](nums []T) T {
18    var total T
19    for _, n := range nums { total += n }
20    return total
21}
22
23sum([]int{1, 2, 3})       // T = int
24sum([]float64{1.1, 2.2})  // T = float64

Syntax breakdown

1func ptr[T any](v T) *T { return &v }
2//       ^^^^^^  ^ ^
3//       |       | return type is also T
4//       |       parameter type is T
5//       type parameter: T is the name, any is the constraint

Constraints can be narrowed

1// Only accept numbers
2func sum[T int | float64](nums []T) T { ... }
3
4// Only accept types that implement String()
5func print[T fmt.Stringer](v T) { ... }

Type inference: usually no need to specify explicitly

1ptr("hello")         // inferred: T = string
2ptr[string]("hello") // explicit, same result

Knowledge Sugar

Generics vs any vs interface

anyinterface (with methods)Generics
Type resolved atRuntimeRuntimeCompile time
Type safetyNoPartialYes
Best forTruly type-agnostic codeConstraining behaviorSame logic across multiple types

When to use Generics? When the same logic needs to work across multiple types and you want compile-time type error detection. Pointer helpers and collection operations (map, filter, reduce) are the most common examples.

#go #til

← Back to Main Page