Why Generics Matter
Overview
Generics (type parameters), introduced in Go 1.18, allow writing functions and types that work with multiple types while maintaining type safety.
The Problem Before Generics
// Without generics: duplicate code or use interface{}
func MinInt(a, b int) int {
if a < b { return a }
return b
}
func MinFloat(a, b float64) float64 {
if a < b { return a }
return b
}
// Or lose type safety
func Min(a, b interface{}) interface{} {
// Requires type assertions, runtime checks
}With Generics
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
Min(1, 2) // int
Min(1.5, 2.5) // float64
Min("a", "b") // stringType Parameters
func Print[T any](v T) {
fmt.Println(v)
}
// Multiple type parameters
func Pair[K, V any](k K, v V) (K, V) {
return k, v
}Constraints
import "golang.org/x/exp/constraints"
// Built-in constraints
any // No requirements
comparable // Supports == and !=
// From constraints package
constraints.Ordered // Supports < > <= >=
constraints.Integer // Int types
constraints.Float // Float typesCustom Constraints
type Number interface {
int | int32 | int64 | float32 | float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}Generic Type Aliases (Go 1.24+)
Go 1.24 introduced support for generic type aliases, allowing you to create aliases for generic types.
type Set[T comparable] map[T]struct{}
// Type alias (Go 1.24+)
type IntSet = Set[int]
type AnySet[T comparable] = Set[T]This is particularly useful for refactoring and maintaining compatibility while migrating to generic implementations.
Benefits
- Type safety: Errors caught at compile time
- No code duplication: One implementation for many types
- Performance: No interface boxing/unboxing
- Better documentation: Types are explicit
Summary
| Before | After |
|---|---|
| Code duplication | Single generic function |
interface{} |
Type parameters |
| Runtime errors | Compile-time errors |
| Type assertions | Direct usage |