1. Use generics when you have type duplication
// ✅ GOOD: Eliminates duplication
func Max[T comparable](a, b T) T {
if a > b {
return a
}
return b
}
// ❌ BAD: Unnecessary generics
func Print[T any](v T) {
fmt.Println(v) // fmt.Println already accepts any
}
2. Prefer constraints over any
// ✅ GOOD: Specific constraint
func Add[T int | float64](a, b T) T {
return a + b
}
// ⚠️ LESS IDEAL: Too permissive
func Add[T any](a, b T) T {
// Can't actually add with any
}
3. Use descriptive type parameter names
// ✅ GOOD: Descriptive names
func Map[Key comparable, Value any](m map[Key]Value) { }
// ⚠️ LESS IDEAL: Generic names
func Map[K comparable, V any](m map[K]V) { }
Idiomatic Go Note While Go allows generics for any repeated code pattern, the idiomatic approach is to use generics only when they eliminate meaningful code duplication. Don't use generics just because you can—sometimes interfaces or concrete types are simpler and more appropriate. Excessive generics can make code harder to understand and maintain.
4. Don't overuse generics
// ❌ BAD: Generics where not needed
func Process[T any](v T) {
fmt.Println(v) // Just use fmt.Println directly
}
// ✅ GOOD: Generics where they add value
type Stack[T any] struct {
items []T
}
5. Consider performance
Generics are compiled to type-specific code, so there's no runtime overhead. However, code size increases with each type used.
6. Use type inference when possible
// ✅ GOOD: Let compiler infer
stack := NewStack[int]() // Type inferred from usage
// ⚠️ VERBOSE: Explicit when not needed
stack := NewStack[int]() // Sometimes explicit is clearer
Common patterns
1. Container types
type Queue[T any] struct {
items []T
}
type Set[T comparable] struct {
items map[T]bool
}
2. Utility functions
func Contains[T comparable](slice []T, value T) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
}
3. Transformations
func Map[T, U any](slice []T, f func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}
Generics are a powerful addition to Go that eliminate code duplication while maintaining type safety. Use them when you have repeated code patterns that differ only by type, but don't overuse them—sometimes interfaces or concrete types are more appropriate.