go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
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
Claude Code
$ npx -y skills add muratmirgun/gophers --skill go-data-structures --agent claude-code
Codex
$ npx skills add muratmirgun/gophers --skill go-data-structures -a codex -y
Cursor
$ npx skills add muratmirgun/gophers --skill go-data-structures -a cursor -y
Gemini CLI
$ npx skills add muratmirgun/gophers --skill go-data-structures -a gemini-cli -y
GitHub Copilot
$ npx skills add muratmirgun/gophers --skill go-data-structures -a github-copilot -y
OpenCode
$ npx skills add muratmirgun/gophers --skill go-data-structures -a opencode -y
Windsurf
$ npx skills add muratmirgun/gophers --skill go-data-structures -a windsurf -y
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.
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by…
Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function…
Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker…
Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation,…
Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.