.agents/skills/go-control-flow/SKILL.md
Go control flow idioms from Effective Go. Covers if with initialization, omitting else for early returns, for loop forms, range, switch without fallthrough, type switch, and blank identifier patterns. Use when writing conditionals, loops, or switch statements in Go.
npx skillsauth add phant-app/phant go-control-flowInstall 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. Go's control structures are related to C but differ in important ways. Understanding these differences is essential for writing idiomatic Go code.
Go has no do or while loop—only a generalized for. There are no
parentheses around conditions, and bodies must always be brace-delimited.
Go's if requires braces and has no parentheses around the condition:
if x > 0 {
return y
}
if and switch accept an optional initialization statement. This is common
for scoping variables to the conditional block:
// Good: err scoped to if block
if err := file.Chmod(0664); err != nil {
log.Print(err)
return err
}
When an if body ends with break, continue, goto, or return, omit the
unnecessary else. This keeps the success path unindented:
// Good: no else, success path at left margin
f, err := os.Open(name)
if err != nil {
return err
}
codeUsing(f)
// Bad: else clause buries normal flow
f, err := os.Open(name)
if err != nil {
return err
} else {
codeUsing(f) // unnecessarily indented
}
Code reads well when the success path flows down the page, eliminating errors as they arise:
// Good: guard clauses eliminate errors early
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
The := short declaration allows redeclaring variables in the same scope under
specific conditions:
f, err := os.Open(name) // declares f and err
// ...
d, err := f.Stat() // declares d, reassigns err (not a new err)
A variable v may appear in a := declaration even if already declared,
provided:
vvThis pragmatic rule makes it easy to reuse a single err variable through a
chain of operations.
// Good: err reused across multiple calls
data, err := fetchData()
if err != nil {
return err
}
result, err := processData(data) // err reassigned, result declared
if err != nil {
return err
}
Warning: If v is declared in an outer scope, := creates a new
variable that shadows it:
// Bad: accidental shadowing
var err error
if condition {
x, err := someFunc() // this err shadows the outer err!
// outer err remains nil
}
Go unifies for and while into a single construct with three forms:
// C-style for (only form with semicolons)
for init; condition; post { }
// While-style (condition only)
for condition { }
// Infinite loop
for { }
Use range to iterate over arrays, slices, strings, maps, and channels:
// Iterate with key and value
for key, value := range oldMap {
newMap[key] = value
}
// Key/index only (drop the second variable)
for key := range m {
if key.expired() {
delete(m, key)
}
}
// Value only (use blank identifier for index)
for _, value := range array {
sum += value
}
For strings, range iterates over UTF-8 encoded runes (not bytes), handling
multi-byte characters automatically.
Go has no comma operator. Use parallel assignment for multiple loop variables:
// Reverse a slice
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
Note: ++ and -- are statements, not expressions, so they cannot be used in
parallel assignment.
Go's switch is more flexible than C's:
break in each case)If the switch has no expression, it switches on true. This is idiomatic for
writing clean if-else-if chains:
// Good: expression-less switch for ranges
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}
Multiple cases can be combined with commas (no fall through needed):
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
return true
}
return false
}
break terminates the switch by default. To break out of an enclosing loop, use
a label:
Loop:
for n := 0; n < len(src); n += size {
switch {
case src[n] < sizeOne:
break // breaks switch only
case src[n] < sizeTwo:
if n+1 >= len(src) {
break Loop // breaks out of for loop
}
}
}
A type switch discovers the dynamic type of an interface value using
.(type):
switch v := value.(type) {
case nil:
fmt.Println("value is nil")
case int:
fmt.Printf("integer: %d\n", v) // v is int
case string:
fmt.Printf("string: %q\n", v) // v is string
case bool:
fmt.Printf("boolean: %t\n", v) // v is bool
default:
fmt.Printf("unexpected type %T\n", v)
}
It's idiomatic to reuse the variable name (v := value.(type)) since the
variable has a different type in each case clause.
When a case lists multiple types (case int, int64:), the variable has the
interface type.
The blank identifier _ discards values. It's like writing to /dev/null.
Discard unwanted values from multi-value expressions:
// Only need the error
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}
// Only need the value (discard ok)
value := cache[key] // simpler: just use single-value form
_, present := cache[key] // when you only need presence check
Never discard errors carelessly:
// Bad: ignoring error will crash if path doesn't exist
fi, _ := os.Stat(path)
if fi.IsDir() { // nil pointer dereference if path doesn't exist
// ...
}
Silence compiler errors temporarily during active development:
import (
"fmt"
"io"
)
var _ = fmt.Printf // silence unused import (remove before committing)
var _ io.Reader
func main() {
fd, _ := os.Open("test.go")
_ = fd // silence unused variable
}
Import a package only for its init() side effects:
import _ "net/http/pprof" // registers HTTP handlers
import _ "image/png" // registers PNG decoder
This makes clear the package is imported only for side effects—it has no usable name in this file.
Verify at compile time that a type implements an interface:
// Verify that *MyType implements io.Writer
var _ io.Writer = (*MyType)(nil)
// Verify that MyHandler implements http.Handler
var _ http.Handler = MyHandler{}
This fails at compile time if the type doesn't implement the interface, catching errors early.
| 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 |
| C-style for | for i := 0; i < n; i++ { } |
| While-style | for condition { } |
| Infinite loop | for { } |
| Range with key+value | for k, v := range m { } |
| Range value only | for _, v := range slice { } |
| Range key only | for k := range m { } |
| 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 |
| Type switch | switch v := x.(type) { } |
| Discard value | _, err := f() |
| Side-effect import | import _ "pkg" |
| Interface check | var _ Interface = (*Type)(nil) |
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.