skills/go-generics/SKILL.md
Use when deciding whether to use Go generics, writing generic functions or types, choosing constraints, or picking between type aliases and type definitions. Also use when a user is writing a utility function that could work with multiple types, even if they don't mention generics explicitly. Does not cover interface design without generics (see go-interfaces).
npx skillsauth add cxuu/golang-skills go-genericsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Start with concrete types. Generalize only when a second type appears.
any and excessive type switching"Write code, don't design types." — Robert Griesemer and Ian Lance Taylor
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
}
| 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 (type Old = new.Name) are rare — use only for package migration
or gradual API refactoring.
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.
// 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.
// 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 { ... }
// 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 { ... }
| 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, | for unions; prefer cmp.Ordered over custom |
| Common pitfall | Don't genericize single-use code or when interfaces suffice |
tools
Use when writing, reviewing, or improving Go test code — including table-driven tests, subtests, parallel tests, test helpers, test doubles, and assertions with cmp.Diff. Also use when a user asks to write a test for a Go function, even if they don't mention specific patterns like table-driven tests or subtests. Does not cover benchmark performance testing (see go-performance).
development
Use when working with Go formatting, line length, nesting, naked returns, semicolons, or core style principles. Also use when a style question isn't covered by a more specific skill, even if the user doesn't reference a specific style rule. Does not cover domain-specific patterns like error handling, naming, or testing (see specialized skills). Acts as fallback when no more specific style skill applies.
development
Use when optimizing Go code, investigating slow performance, or writing performance-critical sections. Also use when a user mentions slow Go code, string concatenation in loops, or asks about benchmarking, even if the user doesn't explicitly mention performance patterns. Does not cover concurrent performance patterns (see go-concurrency).
development
Use when creating Go packages, organizing imports, managing dependencies, or deciding how to structure Go code into packages. Also use when starting a new Go project or splitting a growing codebase into packages, even if the user doesn't explicitly ask about package organization. Does not cover naming individual identifiers (see go-naming).