Suppose a cache must accept phone numbers as keys and names as values, but another cache needs years as keys and collections of medalists as values. Generics let one type declaration serve both cases without giving up type-specific keys and values. section 7.1 section 7.1.3
A type parameter is a placeholder whose concrete type is set when the generic function or type is used. Declare parameters in square brackets: `func prettyPrint[T any](t T)` accepts a value of type `T`. The constraint `any` permits any type, so printing it with `%v` works for both a number and a string; `%f` would assume a floating-point value. Callers can specify a type explicitly, or the compiler can infer it from a function argument. section 7.1.1 section 7.6
Listing 7.1 cache.go
// Cache is key-value storage.
type Cache[K comparable, V any] struct {
data map[K]V
}
The cache declaration shows why different parameters can need different constraints: `K comparable` allows keys in `map[K]V`, while `V any` leaves values unrestricted. section 7.1.3
Choose a constraint for the operations the code must perform. `comparable` permits equality comparisons; an ordered constraint permits ordering comparisons. An interface can also constrain a parameter to types with a required method, as with `fmt.Stringer`, or list permitted underlying types using `~`. A parameterized slice type such as `Group[T any] []T` can have methods that use `T`. section 7.1.1 section 7.6
For the cache, initialize the map in `New[K, V]`, then add methods to read, upsert, and delete entries. Decide whether the map stores `V` or `*V`: the choice affects copying, pointer use, and how values can be updated. Generic parameters do not settle the separate requirement that a shared cache be concurrency safe. Use generics where they remove repetition, not merely because they are available. section 7.1.3 section 7.0



















