Go Declarations and Initialization
Quick Reference: var vs :=
| Context | Use | Example |
|---|---|---|
| Top-level | var (always) | var _s = F() |
| Local with value | := | s := "foo" |
| Local zero-value (intentional) | var | var filtered []int |
| Type differs from expression | var with type | var _e error = F() |
Read references/SCOPE.md when deciding between var and := in complex initialization patterns or multi-return assignments.
Group Similar Declarations
Group related var, const, type in parenthesized blocks. Separate
unrelated declarations into distinct blocks.
// Bad
const a = 1
const b = 2
// Good
const (
a = 1
b = 2
)
Inside functions, group adjacent vars even if unrelated:
var (
caller = c.name
format = "json"
timeout = 5 * time.Second
)
Constants and iota
Start enums at one so the zero value represents invalid/unset:
const (
Add Operation = iota + 1
Subtract
Multiply
)
Use zero when the default behavior is desirable (e.g., LogToStdout).
Read references/IOTA.md when designing iota enums with bitmask patterns, byte-size constants, or String() methods.
Variable Scope
Use if-init to limit scope when the result is only needed for the error check:
if err := os.WriteFile(name, data, 0644); err != nil {
return err
}
Don't reduce scope if it forces deeper nesting or you need the result outside
the if. Move constants into functions when only used there.
Read references/SCOPE.md when working with top-level declarations or choosing between var and := for local variables.
Initializing Structs
- Always use field names (enforced by
go vet). Exception: test tables with ≤3 fields. - Omit zero-value fields — let Go set defaults.
- Use
varfor zero-value structs:var user Usernotuser := User{} - Use
&T{}overnew(T):sptr := &T{Name: "bar"}
Read references/STRUCTS.md when initializing structs with many fields, building slices of struct pointers, or choosing single-line vs multi-line format.
Composite Literal Formatting
Use field names for external package types. Match closing brace indentation
with the opening line. Omit repeated type names in slice/map literals
(gofmt -s).
Read references/INITIALIZATION.md when working with complex composite literals, cuddled braces, or zero-value field decisions.
Read references/LITERALS.md when formatting complex composite literals.
Initializing Maps
| Scenario | Use | Example |
|---|---|---|
| Empty, populated later | make(map[K]V) | m := make(map[string]int) |
| Nil declaration | var | var m map[string]int |
| Fixed entries at init | Literal | m := map[string]int{"a": 1} |
make() visually distinguishes empty-but-initialized from nil. Use size hints
when the count is known.
Raw String Literals
Use backtick strings to avoid hand-escaped characters:
// Bad
wantError := "unknown name:\"test\""
// Good
wantError := `unknown name:"test"`
Ideal for regex, SQL, JSON, and multi-line text.
Prefer any Over interface{}
Go 1.18+: use any instead of interface{} in all new code.
Avoid Shadowing Built-In Names
Never use predeclared identifiers (error, string, len, cap, append,
copy, new, make, close, delete, panic, recover, any, true,
false, nil, iota) as names. Use go vet to detect.
// Bad — shadows the builtin
var error string
// Good
var errorMessage string
Read references/SHADOWING.md when debugging issues where := creates new variables that shadow outer scope.
Related Skills
- Naming conventions: See go-naming when choosing variable names, constant names, or deciding name length by scope
- Data structures: See go-data-structures when choosing between
newandmake, or initializing slices and maps - Control flow scoping: See go-control-flow when using if-init,
:=redeclaration, or avoiding variable shadowing - Capacity hints: See go-performance when pre-allocating maps or slices with known sizes