.agents/skills/go-data-structures/SKILL.md
Go data structures including allocation with new vs make, arrays, slices, maps, printing with fmt, and constants with iota. Use when working with Go's built-in data structures, memory allocation, or formatted output.
npx skillsauth add phant-app/phant go-data-structuresInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Source: Effective Go
This skill covers Go's built-in data structures and allocation primitives.
Go has two allocation primitives: new and make. They do different things.
new(T) allocates zeroed storage for a new item of type T and returns *T:
p := new(SyncedBuffer) // type *SyncedBuffer, zeroed
var v SyncedBuffer // type SyncedBuffer, zeroed
Zero-value design: Design data structures so the zero value is useful without
further initialization. Examples: bytes.Buffer, sync.Mutex.
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
// Ready to use immediately upon allocation
make(T, args) creates slices, maps, and channels only. It returns an
initialized (not zeroed) value of type T (not *T):
make([]int, 10, 100) // slice: length 10, capacity 100
make(map[string]int) // map: ready to use
make(chan int) // channel: ready to use
var p *[]int = new([]int) // *p == nil; rarely useful
var v []int = make([]int, 100) // v is a usable slice of 100 ints
// Idiomatic:
v := make([]int, 100)
Rule: make applies only to maps, slices, and channels and does not return
a pointer.
Create and initialize structs, arrays, slices, and maps in one expression:
// Struct with positional fields
f := File{fd, name, nil, 0}
// Struct with named fields (order doesn't matter, missing = zero)
f := &File{fd: fd, name: name}
// Zero value
f := &File{} // equivalent to new(File)
// Arrays, slices, maps
a := [...]string{Enone: "no error", Eio: "Eio", Einval: "invalid"}
s := []string{Enone: "no error", Eio: "Eio", Einval: "invalid"}
m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid"}
Note: It's safe to return the address of a local variable in Go—the storage survives after the function returns.
Arrays are values in Go (unlike C):
[10]int and [20]int are distinctfunc Sum(a *[3]float64) (sum float64) {
for _, v := range *a {
sum += v
}
return
}
array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Pass pointer for efficiency
Recommendation: Use slices instead of arrays in most cases.
Slices wrap arrays to provide a flexible, powerful interface to sequences.
Slices hold references to an underlying array. Assigning one slice to another makes both refer to the same array:
func (f *File) Read(buf []byte) (n int, err error)
// Read into first 32 bytes of larger buffer
n, err := f.Read(buf[0:32])
len(s): current lengthcap(s): maximum length (from start of slice to end of underlying array)func append(slice []T, elements ...T) []T
Always assign the result—the underlying array may change:
x := []int{1, 2, 3}
x = append(x, 4, 5, 6)
// Append a slice to a slice
y := []int{4, 5, 6}
x = append(x, y...) // Note the ...
Method 1: Independent inner slices (can grow/shrink independently):
picture := make([][]uint8, YSize)
for i := range picture {
picture[i] = make([]uint8, XSize)
}
Method 2: Single allocation (more efficient for fixed sizes):
picture := make([][]uint8, YSize)
pixels := make([]uint8, XSize*YSize)
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
For detailed slice internals, see references/SLICES.md.
Normative: This is required per Go Wiki CodeReviewComments.
When declaring an empty slice, prefer:
var t []string
over:
t := []string{}
The former declares a nil slice, while the latter is non-nil but zero-length.
They are functionally equivalent—their len and cap are both zero—but the nil
slice is the preferred style.
Exception for JSON encoding: A nil slice encodes to null, while an empty
slice []string{} encodes to []. Use non-nil when you need a JSON array:
// nil slice → JSON null
var tags []string
json.Marshal(tags) // "null"
// empty slice → JSON array
tags := []string{}
json.Marshal(tags) // "[]"
Interface design: When designing interfaces, avoid making a distinction between a nil slice and a non-nil zero-length slice, as this can lead to subtle programming errors.
Maps associate keys with values. Keys must support equality (==).
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
}
offset := timeZone["EST"] // -18000
An absent key returns the zero value. Use the "comma ok" idiom to distinguish:
seconds, ok := timeZone[tz]
if !ok {
log.Println("unknown time zone:", tz)
}
// Or combined:
if seconds, ok := timeZone[tz]; ok {
return seconds
}
delete(timeZone, "PDT") // Safe even if key doesn't exist
Use map[T]bool:
attended := map[string]bool{"Ann": true, "Joe": true}
if attended[person] { // false if not in map
fmt.Println(person, "was at the meeting")
}
The fmt package provides rich formatted printing.
| Function | Output |
|----------|--------|
| Printf | Formatted to stdout |
| Sprintf | Returns formatted string |
| Fprintf | Formatted to io.Writer |
| Print/Println | Default format |
fmt.Printf("Hello %d\n", 23)
fmt.Println("Hello", 23)
s := fmt.Sprintf("Hello %d", 23)
%v prints any value with a reasonable default:
fmt.Printf("%v\n", timeZone)
// map[CST:-21600 EST:-18000 MST:-25200 PST:-28800 UTC:0]
For structs:
%v: values only%+v: with field names%#v: full Go syntaxtype T struct {
a int
b float64
c string
}
t := &T{7, -2.35, "abc\tdef"}
fmt.Printf("%v\n", t) // &{7 -2.35 abc def}
fmt.Printf("%+v\n", t) // &{a:7 b:-2.35 c:abc def}
fmt.Printf("%#v\n", t) // &main.T{a:7, b:-2.35, c:"abc\tdef"}
| Format | Purpose |
|--------|---------|
| %T | Type of value |
| %q | Quoted string |
| %x | Hex (strings, bytes, ints) |
Define String() string to control default formatting:
func (t *T) String() string {
return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
}
Warning: Don't call Sprintf with %s on the receiver—infinite recursion:
// Bad: infinite recursion
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", m)
}
// Good: convert to basic type
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", string(m))
}
Constants are created at compile time and can only be numbers, characters, strings, or booleans.
iota creates enumerated constants:
type ByteSize float64
const (
_ = iota // ignore first value (0)
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
)
Combine with String() for automatic formatting:
func (b ByteSize) String() string {
switch {
case b >= EB:
return fmt.Sprintf("%.2fEB", b/EB)
case b >= PB:
return fmt.Sprintf("%.2fPB", b/PB)
// ... etc
}
return fmt.Sprintf("%.2fB", b)
}
Advisory: This is a best practice recommendation from Go Wiki CodeReviewComments.
To avoid unexpected aliasing, be careful when copying a struct from another
package. For example, bytes.Buffer contains a []byte slice. If you copy a
Buffer, the slice in the copy may alias the array in the original, causing
subsequent method calls to have surprising effects.
// Dangerous: copying a bytes.Buffer
var buf1 bytes.Buffer
buf1.WriteString("hello")
buf2 := buf1 // buf2's internal slice may alias buf1's array!
buf2.WriteString(" world") // May affect buf1 unexpectedly
General rule: Do not copy a value of type T if its methods are associated
with the pointer type *T.
This applies to many types in the standard library and third-party packages:
bytes.Buffersync.Mutex, sync.WaitGroup, sync.Cond// Bad: copying a mutex
var mu sync.Mutex
mu2 := mu // Copying a mutex is almost always a bug
// Good: use pointers or embed carefully
type SafeCounter struct {
mu sync.Mutex
count int
}
// Pass by pointer, not by value
func increment(sc *SafeCounter) {
sc.mu.Lock()
sc.count++
sc.mu.Unlock()
}
| Topic | Key Point |
|-------|-----------|
| new(T) | Returns *T, zeroed |
| make(T) | Slices, maps, channels only; returns T, initialized |
| Arrays | Values, not references; size is part of type |
| Slices | Reference underlying array; use append |
| Maps | Key must support ==; use comma-ok for presence |
| Copying | Don't copy T if methods are on *T; beware aliasing |
| %v | Default format for any value |
| %+v | Struct with field names |
| %#v | Full Go syntax |
| iota | Enumerated constants |
data-ai
Beginner-first implementation planning from an approved design. Use after brainstorming, or when the user already approved requirements and wants a step-by-step Laravel implementation plan before coding.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
development
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
development
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.