agentsclimarketplace

Go data structures

Skill muratmirgun/gophers/skills/go-data-structures

26 production-grade Go skills for Claude Code, Gemini CLI, and opencode.

Install
npx -y skills add muratmirgun/gophers --skill go-data-structures

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

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.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.4 KB, as published. Nobody here has run it

Go Data Structures

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.

Core Rules

  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.

Picking a Structure

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

Slice Internals

A slice is a 3-word header: pointer, length, capacity. Multiple slices can alias the same backing array — s[1:4] shares memory with s.

Capacity Growth

The exact algorithm has changed across versions; do not rely on it. As of recent Go:

  • len < 256 → capacity roughly doubles.
  • len ≥ 256 → grows by ~25%.
  • Each growth allocates a new backing array and copies — O(n) per growth.

Preallocation

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+)

slices Package (Go 1.21+)

FunctionPurpose
Sort, SortFunc, SortStableFuncsorting
BinarySearch, BinarySearchFuncsorted lookup
Contains, Index, IndexFuncsearch
Compact, CompactFuncdedupe adjacent equals
Clone, Equalsafe copy / comparison
Delete, DeleteFuncremoval preserving order
Growpreallocate 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 for capacity growth, aliasing pitfalls, and 2-D slice patterns.

nil vs Empty Slice: The JSON Trap

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

Maps are hash tables with 8-entry buckets and overflow chains. They are reference types — assigning a map copies a pointer.

Preallocation

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.

Sets

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.

maps Package (Go 1.21+)

Clone, Equal/EqualFunc, DeleteFunc; Keys, Values, Collect, Insert since 1.23 (iterators).

Read references/strings-bytes-builder.md for string-vs-bytes, Builder vs Buffer, and rune handling.

Arrays

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 keys

For anything dynamic, use a slice.

container/* and Third-Party

PackageUse caseCaveat
container/heappriority queue, top-Kimplement the interface yourself
container/listLRU, frequent middle splicepoor cache locality
container/ringrolling window, round-robinfixed size
bufioI/O with many small reads/writesalways check Flush errors

For typed sets/queues/trees beyond the stdlib, prefer well-tested libraries (emirpasic/gods, gammazero/deque) and benchmark before optimising.

Read references/containers-and-pointers.md for heap implementation, unsafe.Pointer's six valid patterns, and weak.Pointer[T].

Copy Semantics Cheat Sheet

TypeCopy behaviour
primitives, arrays, structsvalue (deep for contained value fields)
sliceheader copied, backing array shared — use slices.Clone
map, channelreference copied — use maps.Clone for maps
*T, interfaceaddress / (type, value) pair copied

Anti-Patterns

Anti-patternWhy it hurtsDo this instead
s := append(s, x) ignoring returnBacking array may move; s becomes staleAlways reassign
var m map[K]V; m[k] = vnil map panicm := make(map[K]V) or map[K]V{}
var s []T then marshal to JSON as []Encodes as nulls := []T{}
make([]T, 0, 10000) "just in case"Wasted memorySize by actual data
m := map[K]bool{} as a setfalse is ambiguousmap[K]struct{}
bytes.Buffer for pure string buildingExtra copy in String()strings.Builder
Large struct values in a mapEach lookup copies the valuemap[K]*V

Verification Checklist

  • Every make([]T, ...) and make(map[K]V, ...) has a capacity hint when the size is known.
  • Every append reassigns its result.
  • Slices marshaled to JSON are initialised as []T{}, not var s []T.
  • All "sets" use map[K]struct{} (or a generic Set[T] wrapper).
  • No bytes.Buffer used purely for String() output.
  • No *sync.Mutex copied via struct assignment (go vet copylocks).
  • slices.Clone / maps.Clone used when handing data to callers that may mutate.

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.