go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase, Repository, Delivery layers, inward…
Use when choosing or operating on Go slices, maps, arrays, strings, or container/* types — including slice internals, capacity growth, preallocation, map buckets, sets via map[T]struct{}, strings.Builder vs bytes.Buffer, generic containers, and the slices/maps standard packages
$ npx -y skills add muratmirgun/gophers --skill go-data-structures --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/go-data-structuresContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when choosing or operating on Go slices, maps, arrays, strings, or container/* types — including slice internals, capacity growth, preallocation, map buckets, sets via map[T]struct{}, strings.Builder vs bytes.Buffer, generic containers, and the slices/maps standard packages
name: go-data-structures
description: "Use when choosing or operating on Go slices, maps, arrays, strings, or container/* types — including slice internals, capacity growth, preallocation, map buckets, sets via map[T]struct{}, strings.Builder vs bytes.Buffer, generic containers, and the slices/maps standard packages (Go 1.21+). Apply proactively whenever data is being collected, transformed, or copied, even if the user has not asked about allocation."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. slices/maps packages need Go 1.21+; iterator helpers need 1.23+; weak.Pointer needs 1.24+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)Pick the structure that fits the access pattern — not the most familiar one. Slices and maps are the workhorses; arrays, container types, and the `slices`/`maps` packages cover the rest. Understanding the **header layout**, **growth costs**, and **copy semantics** of each turns most performance questions into one-line decisions.
1. **Slices and maps are reference types** — assigning copies the header, not the data. Use `slices.Clone` / `maps.Clone` for a true copy. 2. **Preallocate** with `make([]T, 0, n)` and `make(map[K]V, n)` whenever the size is known or estimable. 3. **Always assign the result of `append`** — the backing array may move. 4. **Use `slices` and `maps` packages** (Go 1.21+) instead of hand-rolled helpers. 5. **`map[K]struct{}` is the canonical set** — zero-byte values, no boolean ambiguity. 6. **`strings.Builder` for string building**, `bytes.Buffer` when you need `io.Reader`/`io.Writer`.
What do you need?
├─ Ordered, fixed compile-time size → [N]T array
├─ Ordered, dynamic size → []T slice
│ ├─ Known size → make([]T, 0, n)
│ └─ JSON output must be [] → []T{} literal (not nil)
├─ Key/value lookup → map[K]V
│ ├─ Need a set → map[K]struct{}
│ └─ Known size → make(map[K]V, n)
├─ Priority queue / top-k → container/heap
├─ Frequent middle insertion → container/list
├─ Fixed-size rolling window → container/ring
├─ Pure string building → strings.Builder
└─ Read+write of bytes → bytes.BufferA slice is a 3-word header: pointer, length, capacity. Multiple slices can alias the same backing array — `s[1:4]` shares memory with `s`.
The exact algorithm has changed across versions; do **not** rely on it. As of recent Go:
users := make([]User, 0, len(ids)) // exact size results := make([]Result, 0, estimated) // approximate s = slices.Grow(s, additional) // pre-grow before bulk append (Go 1.21+)
| Function | Purpose | |---|---| | `Sort`, `SortFunc`, `SortStableFunc` | sorting | | `BinarySearch`, `BinarySearchFunc` | sorted lookup | | `Contains`, `Index`, `IndexFunc` | search | | `Compact`, `CompactFunc` | dedupe adjacent equals | | `Clone`, `Equal` | safe copy / comparison | | `Delete`, `DeleteFunc` | removal preserving order | | `Grow` | preallocate before append | | `Concat` (1.22+) | concatenate slices |
Prefer these over hand-rolled loops — they're tested, generic, and use the fastest available paths.
> Read [references/slices-and-maps.md](references/slices-and-maps.md) for capacity growth, aliasing pitfalls, and 2-D slice patterns.
Both have `len == 0` and `cap == 0`, but they encode differently:
var nilSlice []string // → JSON: null
emptySlice := []string{} // → JSON: []API contracts almost always want `[]`. **Initialise the slice explicitly** in any struct that gets marshaled to JSON, and treat nil/empty as identical when *reading* (use `len(s) == 0`).
For internal computation where nil is never marshaled, the nil slice is conventional and slightly cheaper (no allocation until first append).
Maps are hash tables with 8-entry buckets and overflow chains. They are reference types — assigning a map copies a pointer.
m := make(map[string]*User, len(users)) // avoids rehashing during population
The size hint is *approximate* (it's about bucket count), but it still saves repeated rehashing in the common case.
type Set[T comparable] map[T]struct{}
func (s Set[T]) Add(v T) { s[v] = struct{}{} }
func (s Set[T]) Has(v T) bool { _, ok := s[v]; return ok }
func (s Set[T]) Remove(v T) { delete(s, v) }`struct{}` is zero bytes; the set is just the key set of the underlying map.
`map[K]bool` is also common but ambiguous: did `false` mean "explicitly excluded" or "not present"? `struct{}` removes the question.
`Clone`, `Equal`/`EqualFunc`, `DeleteFunc`; `Keys`, `Values`, `Collect`, `Insert` since 1.23 (iterators).
> Read [references/strings-bytes-builder.md](references/strings-bytes-builder.md) for string-vs-bytes, `Builder` vs `Buffer`, and rune handling.
Fixed-size, value type, copied on assignment. Useful for compile-time-known sizes:
type Digest [32]byte
type IP4 [4]byte
cache := map[[2]int]Result{} // arrays are comparable → usable as map keysFor anything dynamic, use a slice.
| Package | Use case | Caveat | |---|---|---| | `container/heap` | priority queue, top-K | implement the interface yourself | | `container/list` | LRU, frequent middle splice | poor cache locality | | `container/ring` | rolling window, round-robin | fixed size | | `bufio` | I/O with many small reads/writes | always check `Flush` errors |
For typed sets/queues/trees beyond the std
26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.
Repo: muratmirgun/gophers
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase, Repository, Delivery layers, inward…
Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by topic — formatting, errors,…
Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function design. Covers the priority…
Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker pools, or fan-out/fan-in…
Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation, request-scoped values,…
Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization, guard clauses, early returns,…