Go Generics and Type Parameters When to Use Generics Start with concrete types. Generalize only when a second type appears. Prefer Generics When Multiple types share identical logic (sorting, filtering, map/reduce) You would otherwise rely on any and excessive type switching You are building a reusable data structure (concurrent-safe set, ordered map) Avoid Generics When Only one type is being instantiated in practice Interfaces already model the shared behavior cleanly The generic code is harder to read than the type-specific alternative "Write code, don't design types." — Robert Griesemer and Ian Lance Taylor Decision Flow Do multiple types share identical logic? ├─ No → Use concrete types ├─ Yes → Do they share a useful interface? │ ├─ Yes → Use an interface │ └─ No → Use generics Bad: // Premature generics: only ever called with int func Sum [ T constraints . Integer | constraints . Float ] ( vals [ ] T ) T { var total T for _ , v := range vals { total += v } return total } Good: func SumInts ( vals [ ] int ) int { var total int for _ , v := range vals { total += v } return total } Type Parameter Naming Name Typical Use T General type parameter K Map key type V Map value type E Element/item type For complex constraints, a short descriptive name is acceptable: func Marshal [ Opts encoding . MarshalOptions ] ( v any , opts Opts ) ( [ ] byte , error ) Type Aliases vs Type Definitions Type aliases ( type Old = new.Name ) are rare — use only for package migration or gradual API refactoring. Constraint Composition Combine constraints with ~ (underlying type) and | (union): type Numeric interface { ~ int | ~ int8 | ~ int16 | ~ int32 | ~ int64 | ~ float32 | ~ float64 } func Sum [ T Numeric ] ( vals [ ] T ) T { var total T for _ , v := range vals { total += v } return total } Use the constraints package or cmp package (Go 1.21+) for standard constraints like cmp.Ordered instead of writing your own. Read references/CONSTRAINTS.md when writing custom type constraints, composing constraints with ~ and |, or debugging type inference issues. Common Pitfalls Don't Wrap Standard Library Types // Bad: generic wrapper adds complexity without value type Set [ T comparable ] struct { m map [ T ] struct { } } // Better: use map[T]struct{} directly when the usage is simple seen := map [ string ] struct { } { } Generics justify their complexity when they eliminate duplication across multiple call sites . A single-use generic is just indirection. Don't Use Generics for Interface Satisfaction // Bad: T is only used to satisfy an interface — just use the interface func Process [ T io . Reader ] ( r T ) error { ... } // Good: accept the interface directly func Process ( r io . Reader ) error { ... } Avoid Over-Constraining // Bad: constraint is more restrictive than needed func Contains [ T interface { ~ int | ~ string } ] ( slice [ ] T , target T ) bool { ... } // Good: comparable is sufficient func Contains [ T comparable ] ( slice [ ] T , target T ) bool { ... } Quick Reference Topic Guidance When to use generics Only when multiple types share identical logic and interfaces don't suffice Starting point Write concrete code first; generalize later Naming Single uppercase letter ( T , K , V , E ) Type aliases Same type, alternate name; use only for migration Constraint composition Use ~ for underlying types, ` Common pitfall Don't genericize single-use code or when interfaces suffice
go-generics
安装
npx skills add https://github.com/cxuu/golang-skills --skill go-generics