For statements
Go has one looping construct, but initializer scope, post statements, and range behavior all have distinct rules.
Canonical guidance
- plain
for, condition-onlyfor, andfor rangeare all forms of one statement - initializer scope and loop variable behavior matter for later references
breakandcontinuework with labels when nested control flow needs clarity
Use when
- reviewing loop structure
- diagnosing capture or scope bugs
- explaining labeled control flow
Avoid
- assuming loop variables outlive their scope
- using labels when simpler structure would do
- mixing complex mutation into loop headers without clarity
Preferred pattern
for i := 0; i < len(s); i++ {
use(s[i])
}
Anti-pattern
- capturing loop variables in deferred or asynchronous work without checking what variable instance later code observes
Explanation: This is tempting because the closure is nearby, but loop variable semantics deserve explicit review whenever execution is deferred.
Why
- Go loop semantics are simple on the surface and still produce high-value compile-time and review questions