SKILL.md
$27
If you need the variable beyond a few lines after the if, declare it
separately and use a standard if instead:
x, err := f()
if err != nil {
return err
}
// lots of code that uses x
Indent Error Flow (Guard Clauses)
When an if body ends with break, continue, goto, or return, omit the
unnecessary else. Keep the success path unindented:
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
Never bury normal flow inside an else when the if already returns.
Redeclaration and Reassignment
The := short declaration allows redeclaring variables in the same scope:
f, err := os.Open(name) // declares f and err
d, err := f.Stat() // declares d, reassigns err
A variable v may appear in a := declaration even if already declared,
provided:
- The declaration is in the same scope as the existing
v
- The value is assignable to
v
- At least one other variable is newly created by the declaration
Variable Shadowing
Warning: If v is declared in an outer scope, := creates a new
variable that shadows it — a common source of bugs:
// Bug: ctx inside the if block shadows the outer ctx
if *shortenDeadlines {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
}
// ctx here is still the original — the shadowed ctx didn't escape
// Fix: use = instead of :=
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
For Loops
Go's for is its only looping construct, unifying while, do-while, and
C-style for:
// Condition-only (Go's "while")
for x > 0 {
x = process(x)
}
// Infinite loop
for {
if done() { break }
}
// C-style three-component
for i := 0; i < n; i++ { ... }
Range
range iterates over slices, maps, strings, and channels:
for i, v := range slice { ... } // index + value
for k, v := range myMap { ... } // key + value (non-deterministic order)
for i, r := range "héllo" { ... } // byte index + rune (not byte)
for v := range ch { ... } // receives until channel closed
Key rules:
- Range over strings yields runes, not bytes —
iis the byte offset
- Range over maps has non-deterministic order — don't rely on it
- Use
_to discard the index or value:for _, v := range slice
Parallel Assignment
Go has no comma operator. Use parallel assignment for multiple loop variables:
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
++ and -- are statements, not expressions — they cannot appear in parallel
assignment.
Switch: Labeled Break
break inside a switch within a for loop only breaks the switch.
Use a labeled break to exit the enclosing loop:
Loop:
for _, v := range items {
switch v.Type {
case "done":
break Loop // breaks the for loop
}
}
For type switches, see go-interfaces: Type Switch.
The Blank Identifier
Never discard errors carelessly — a nil dereference panic may follow.
Verify interface compliance at compile time: var _ io.Writer = (*MyType)(nil).
See go-interfaces for the interface satisfaction check pattern.
Quick Reference
Pattern
Go Idiom
If initialization
if err := f(); err != nil { }
Early return
Omit else when if body returns
Redeclaration
:= reassigns if same scope + new var
Shadowing trap
:= in inner scope creates new variable
Parallel assignment
i, j = i+1, j-1
Expression-less switch
switch { case cond: }
Comma cases
case 'a', 'b', 'c':
No fallthrough
Default behavior (explicit fallthrough if needed)
Break from loop in switch
break Label
Discard value
_, err := f()
Side-effect import
import _ "pkg"
Interface check
var _ Interface = (*Type)(nil)
Related Skills
- Error flow: See go-error-handling when structuring guard clauses, early returns, or error-first patterns
- Type switches: See go-interfaces when using type switches, the comma-ok idiom, or interface satisfaction checks
- Nesting reduction: See go-style-core when reducing nesting depth or resolving formatting questions
- Variable scoping: See go-declarations when using if-init,
:=redeclaration, or reducing variable scope