Go by Example

For

Go's single loop construct - the for statement covers basic loops, while-style loops, infinite loops, and range iteration.

Go has exactly one loop keyword: for. It handles every looping pattern - from a classic counter loop to an infinite loop to iterating over collections with range. There is no while or do/while.

The classic three-component for loop: initialiser, condition, post statement. All three are optional.

package main
 
import "fmt"
 
func main() {
    // Classic counter loop
    for i := 0; i < 3; i++ {
        fmt.Println(i)
    }
    // 0
    // 1
    // 2
}

With only a condition, for behaves like while in other languages.

package main
 
import "fmt"
 
func main() {
    n := 1
    for n < 100 {
        n *= 2
    }
    fmt.Println(n) // 128
}

With no condition at all, for loops forever. Break out with break or return from the function.

package main
 
import "fmt"
 
func main() {
    i := 0
    for {
        if i >= 3 {
            break
        }
        fmt.Println(i)
        i++
    }
}

range iterates over slices, arrays, maps, strings, and channels. It returns an index (or key) and a value on each iteration. Use _ to discard either.

package main
 
import "fmt"
 
func main() {
    nums := []int{10, 20, 30}
 
    for i, v := range nums {
        fmt.Println(i, v)
    }
    // 0 10
    // 1 20
    // 2 30
 
    // Discard the index
    for _, v := range nums {
        fmt.Println(v)
    }
 
    // range over a map (iteration order is random)
    m := map[string]int{"a": 1, "b": 2}
    for k, v := range m {
        fmt.Println(k, v)
    }
}

In production

range over a slice gives a copy of each element - assigning to the range variable does not mutate the slice. Use the index form for i := range s { s[i] = ... } to mutate in place. This surprises engineers coming from Python or JavaScript where the range variable is a reference. The same trap applied inside goroutines on Go versions before 1.22: capturing the range variable in a goroutine captured the variable, not its value at that iteration. Go 1.22 changed loop-variable scoping so each iteration gets its own variable, fixing the race automatically. On older toolchains, pass the loop variable as a function argument to get a copy.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free