go-data-idioms
Installation
SKILL.md
Go Data Idioms
Slices are (ptr, len, cap) headers over a shared backing array; aliasing is the default. defer evaluates args at the defer site, not at return. Design types so the zero value is usable.
new vs make
| Call | Returns | For |
|---|---|---|
new(T) |
*T, zeroed |
structs, ints, arrays |
make(T, …) |
T, initialized |
slices, maps, channels |
new([]int) yields *[]int pointing to a nil slice — rarely useful. Use make([]int, n).
Slices: aliasing and append
s := []int{1, 2, 3, 4}
t := s[1:3] // shares backing; t[0]=9 mutates s[1]
u := append(s, 5) // may or may not reallocate
s = append(s, x) // MUST reassign
append(s, x) // lost — returned header discarded
Reallocation when len + added > cap. Before realloc, original and returned share memory; after, they don't. Treat the return as authoritative.
Maps: comma-ok for absence
v, ok := m[k] // ok=false means absent
_, ok := m[k] // presence-only
Missing key silently returns zero value; without ok you cannot tell "absent" from "present and zero".
Defer: LIFO and argument capture
for i := 0; i < 3; i++ {
defer fmt.Println(i) // args evaluated NOW → 2, 1, 0
}
x := 1
defer func() { fmt.Println(x) }()
x = 2 // prints 2 — closure reads at call time
Direct-call defer captures args at the defer statement. A func(){} literal reads variables when it runs.
Named returns + defer = post-return mutation
func parse() (out T, err error) {
defer func() {
if err != nil { err = fmt.Errorf("parse: %w", err) }
}()
...
return // defer sees and rewrites err
}
Works only with named returns. Defer runs after return operands evaluate, before control leaves the frame.
Zero-value-useful
type Buf struct {
mu sync.Mutex // zero: unlocked
buf bytes.Buffer // zero: empty, usable
}
var b Buf // no constructor needed
Pitfalls
- Forgetting to reassign
append: silent element loss when cap is exceeded. - Slice retention:
big[0:1]keeps the whole backing array alive.copyinto a fresh slice when keeping a small prefix. deferin a loop: all defers run at function exit, not per iteration — handles accumulate. Wrap iteration in a helper.- Closure vs argument capture:
defer f(x)captures x now;defer func(){ f(x) }()captures x at execution. - Nil map write panics:
var m map[string]int; m["k"] = 1panics. Alwaysmakebefore writing. - Two-dim slice allocation: one contiguous
make([]T, x*y)sliced into rows beats per-rowmakewhen rows have fixed size.
Weekly Installs
2
Repository
narenaryan/agent-skillsFirst Seen
1 day ago
Security Audits
Installed on
amp2
cline2
opencode2
cursor2
kimi-cli2
warp2