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.…

INSTALLATION
npx skills add https://github.com/cxuu/golang-skills --skill go-generics
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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

Related Skills

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

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card