cxuu/golang-skills

go-generics

Use when deciding whether to use Go generics, writing generic functions or types, choosing constraints, or picking between type aliases and type definitions.

First seen Mar 14, 2026

Installation

$ npx skills add cxuu/golang-skills --skill go-generics

Summary

  • 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).

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from cxuu/golang-skills · top by installs.

npx skills add cxuu/golang-skills

Browse all from cxuu/golang-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 158
License LICENSE
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,954 B
  • docs SUMMARY.md 3,869 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 852 installs

SKILL.md

Go Generics and Type Parameters

Compatibility: Generics require Go 1.18+.

Resource Routing

  • references/CONSTRAINTS.md - Read when composing constraints, using type sets, or choosing between generics and interfaces.

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.


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, ` for unions; prefer cmp.Ordered` over custom
Common pitfall Don't genericize single-use code or when interfaces suffice

Related Skills

  • Interfaces vs generics: See [go-interfaces](../go-interfaces/SKILL.md) when deciding whether an interface already models the shared behavior without generics
  • Type declarations: See [go-declarations](../go-declarations/SKILL.md) when defining new types, type aliases, or choosing between type definitions and aliases
  • Documenting generic APIs: See [go-documentation](../go-documentation/SKILL.md) when writing doc comments and runnable examples for generic functions
  • Naming type parameters: See [go-naming](../go-naming/SKILL.md) when choosing names for type parameters or constraint interfaces