When the same operation is needed for several types, copying its implementation creates boilerplate. Generics let a function, type, or interface take type parameters, so one implementation can serve different types while preserving compile-time type safety. They avoid the type assertions that type-agnostic code built around interface{} often required. section 6.0 section 6.1
func Filter[T any](items []T, keep func(T) bool) []T {
var result []T
for _, item := range items {
if keep(item) {
result = append(result, item)
}
}
return result
}
A single Filter[T any] replaces separate filtering functions for recipes and steps. T appears in both the input slice and predicate, keeping their element types aligned; the compiler can infer T from a call. section 6.1
The constraint after a type parameter determines which types are accepted and which operations the implementation may use. any imposes no specific operation; comparable permits equality checks, as needed when a linked list searches for an item. Arithmetic needs an explicit constraint covering types that support it. A constraint based on an interface exposes only that interface’s methods. section 6.1 section 6.5.2 section 6.5.3
Type parameters can carry the same guarantees through larger abstractions:
- A LinkedList[T] stores T values in nodes, implements collection operations, and can be constructed as NewLinkedList[Recipe](). section 6.1 section 6.3
- A HashMap[K comparable, V any] keeps key and value types distinct while wrapping a built-in map. Its key constraint supports map lookup. section 6.4
- A Transform[T any, U any] function converts a slice of one type into another—for example, extracting string titles from recipes. section 6.2
type HashMap[K comparable, V any] struct { #1
m map[K]V #2
}
func NewHashMap[K comparable, V any]() *HashMap[K, V] { #3
return &HashMap[K, V]{m: make(map[K]V)} #4
}
The constructor and struct show how K and V travel together from declaration to instantiated map storage. section 6.4
Use a generic abstraction when identical logic serves multiple types or an API must preserve type relationships. Prefer concrete code when there is only one use or the implementations differ; prefer a plain interface when behavior matters more than exact type identity. A useful check is whether two independent call sites need different type arguments. section 6.5.4
Keep performance decisions evidence-based: benchmark hot paths, avoid unnecessary interface conversions, and use a narrow constraint when operations require one. Type inference can fail or need explicit type arguments. Generic and nongeneric code can coexist, so adoption need not be a rewrite. section 6.5.1 section 6.5.3 section 6.5.5
Before writing a collection helper, check the standard library: slices offers type-safe sorting, including SortFunc with a comparator for recipe serving sizes; maps offers operations such as Equal and Keys. section 6.6.2



















