if/else, switch, the lone for loop, break, continue.
The for Loop (Go has only one)
Go has exactly one loop keyword: for. There is no while, no do, no
foreach. But for has four forms.
1. C-style for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
2. While form (just the condition)
n := 10
for n > 0 {
n--
}
3. Infinite loop
for {
if done() {
break
}
}
Use break to exit and continue to skip to the next iteration.
4. Range over an integer (Go 1.22+)
Since Go 1.22 you can range over an int to loop that many times — no counter
variable needed:
for i := range 5 {
fmt.Println(i) // 0, 1, 2, 3, 4
}
Equivalent to the C-style form but tidier when you don't have a collection.
5. for ... range — iterate over a collection
This is the workhorse. It works for arrays, slices, maps, strings, and channels.
| Collection | Range yields |
| ---------- | ----------------------------- |
| Array / slice | index, value |
| Map | key, value (random order!) |
| String | byte-index, rune |
| Channel | one value at a time |
Loop variables in closures (the famous gotcha)
Before Go 1.22, the loop variable was shared across iterations. Every closure
captured the same i and saw the final value:
// Before Go 1.22: prints "3 3 3"
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }()
}
Since Go 1.22, each iteration gets its own fresh copy, so the above prints
0 1 2 in some order. If you're on older Go, work around it by shadowing:
for i := 0; i < 3; i++ {
i := i // shadow with a per-iteration copy
go func() { fmt.Println(i) }()
}
Labels with break and continue
For nested loops you can label them and break out of an outer one: