Go has only for loop, but it’s versatile.

Complete, C-style for

for i := 0; i < 10; i++ {
    fmt.Println(i)
}

It has 3 statements: init, comparison and increment.

Variable in the init statements is scoped to the for loop.

:= is required in the init statement, var is not legal here.

The init and post statements are optional:

i := 0
for ; i < 10; i++ {
    fmt.Println(i)
}
 
for i := 0; i < 10; {
    fmt.Println(i)
    if i % 2 == 0 {
        i++
    } else {
        i+=2
    }
}

Condition-only for

Similar to while loops in other languages.

i := 1
for i < 100 {
        fmt.Println(i)
        i = i * 2
}

Infinite for

for {
}

Range for

To iterate over a slice or a map range form can be used. It returns two values: index and value (copy of the element at that index).

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
	fmt.Printf("2**%d = %d\n", i, v)
}

Use _ for either index or value if it’s not needed:

for i, _ := range pow
for _, value := range pow

If only one value is specified, you’ll get only the index:

for i := range pow

Can iterate only over the built-in compound types and user-defined types that are based on them.

It iterates over copy, so the value can’t be changed in place:

evenVals := []int{2, 4, 6, 8, 10, 12}
for _, v := range evenVals {
    v *= 2
}
fmt.Println(evenVals) // [2 4 6 8 10 12] - didn't change

Map order

Order of the iteration over a map is randomized each time to prevent developers relying on the specific order and to prevent Hash DoS attack. But when printing the map, Go outputs keys in the ascending order.

Strings

Iterating over strings has the same nuances described in Indexing. Go will iterate over runes in the string, not bytes, and will offset the index by the number of bytes, see example below:

samples := []string{"hello", "apple_Ο€!"}
for _, sample := range samples {
    for i, r := range sample {
        fmt.Println(i, r, string(r))
    }
    fmt.Println()
}
// First string:
// 0 104 h
// 1 101 e
// 2 108 l
// 3 108 l
// 4 111 o
// Second string:
// 0 97 a
// 1 112 p
// 2 112 p
// 3 108 l
// 4 101 e
// 5 95 _
// 6 960 Ο€ - number 7 is skipped
// 8 33 !

break and continue

Works similar to Python.

Can use labels to control what loop to target:

func main() {
    samples := []string{"hello", "apple_Ο€!"}
outer:
    for _, sample := range samples {
        for i, r := range sample {
            fmt.Println(i, r, string(r))
            // Skips to the next word in the slice when hit `l` rune.
            if r == 'l' {
                continue outer
            }
        }
        fmt.Println()
    }
}
// 0 104 h
// 1 101 e
// 2 108 l
// 0 97 a
// 1 112 p
// 2 112 p
// 3 108 l