go-data-structures — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited go-data-structures (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
slices.Clone / maps.Clone for a true copy.make([]T, 0, n) and make(map[K]V, n) whenever the size is known or estimable.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:
len < 256 → capacity roughly doubles.len ≥ 256 → grows by ~25%.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+)| 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 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 populationThe 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.
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,BuildervsBuffer, 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 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, andweak.Pointer[T].
| Type | Copy behaviour |
|---|---|
| primitives, arrays, structs | value (deep for contained value fields) |
| slice | header copied, backing array shared — use slices.Clone |
| map, channel | reference copied — use maps.Clone for maps |
*T, interface | address / (type, value) pair copied |
| Anti-pattern | Why it hurts | Do this instead |
|---|---|---|
s := append(s, x) ignoring return | Backing array may move; s becomes stale | Always reassign |
var m map[K]V; m[k] = v | nil map panic | m := make(map[K]V) or map[K]V{} |
var s []T then marshal to JSON as [] | Encodes as null | s := []T{} |
make([]T, 0, 10000) "just in case" | Wasted memory | Size by actual data |
m := map[K]bool{} as a set | false is ambiguous | map[K]struct{} |
bytes.Buffer for pure string building | Extra copy in String() | strings.Builder |
| Large struct values in a map | Each lookup copies the value | map[K]*V |
make([]T, ...) and make(map[K]V, ...) has a capacity hint when the size is known.append reassigns its result.[]T{}, not var s []T.map[K]struct{} (or a generic Set[T] wrapper).bytes.Buffer used purely for String() output.*sync.Mutex copied via struct assignment (go vet copylocks).slices.Clone / maps.Clone used when handing data to callers that may mutate.slices/maps packagesstrings.Builder, bytes.Buffer, rune handlingcontainer/heap, generic wrappers, unsafe.Pointer, weak.Pointer~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.