9. `for`: the only loop

📖 Reading · 9 min
💡 Most code boxes below are live — edit one and hit Run. Boxes without a Run button are reference-only (they can't run in your browser).

Most languages have for, while, do-while, and a few more. Go has one loop keyword — for — and it wears all those hats. Learn its four shapes and you've learned every loop in the language.

1. The classic three-part for

for init; condition; post { ... } — set up, test before each pass, and run a step after each pass:

package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println("i is", i)
    }
}

i++ increments i by one. (Go has ++ and --, but they're statements, not expressions — you can't write x := i++.) As always: no parentheses, mandatory braces.

2. for as a "while" loop

Drop the init and post parts, keep just a condition, and for behaves like a while:

package main

import "fmt"

func main() {
    n := 1
    for n < 100 {
        n = n * 2
    }
    fmt.Println("first power of two >= 100:", n)
}

3. The infinite loop

No condition at all means loop forever — you exit with break (or return). This is the standard shape for "keep going until something happens":

package main

import "fmt"

func main() {
    count := 0
    for {
        count++
        if count == 3 {
            break // jump out of the loop entirely
        }
    }
    fmt.Println("broke out at", count)
}

break leaves the loop. Its partner continue skips the rest of the current pass and jumps to the next one:

package main

import "fmt"

func main() {
    // print only the odd numbers under 10
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue // even → skip to the next i
        }
        fmt.Print(i, " ")
    }
    fmt.Println()
}

4. for ... range

The fourth shape iterates over a collection — a slice, a map, a string. We'll meet slices and maps properly soon, but here's the shape so it's familiar:

package main

import "fmt"

func main() {
    names := []string{"Ada", "Grace", "Katherine"}

    for index, name := range names {
        fmt.Printf("%d: %s\n", index, name)
    }
}

range hands you two values each pass: the index and the value. Don't need the index? Use the blank identifier _ to ignore it — remember, an unused variable is a compile error, and _ is how you say "I don't want this one":

package main

import "fmt"

func main() {
    names := []string{"Ada", "Grace", "Katherine"}

    for _, name := range names {
        fmt.Println(name)
    }
}

Since Go 1.22 you can also range over a plain integer to repeat N times. It's a newer convenience the in-browser interpreter doesn't support yet, so this box is reference-only — but it compiles and runs on any recent Go:

for i := range 3 {
    fmt.Println("pass", i) // prints pass 0, pass 1, pass 2
}

Until then, the classic for i := 0; i < 3; i++ does the exact same job and runs everywhere.

Loops nest

A loop inside a loop — the outer runs once for each full pass of the inner:

package main

import "fmt"

func main() {
    for row := 1; row <= 3; row++ {
        for col := 1; col <= 3; col++ {
            fmt.Printf("%d ", row*col)
        }
        fmt.Println()
    }
}

Your turn

Sum the integers from 1 to 100 (inclusive) in a loop and print exactly:

Sum 1..100 = 5050
package main

import "fmt"

func main() {
    sum := 0
    // loop from 1 to 100, adding each to sum
    fmt.Printf("Sum 1..100 = %d\n", sum)
}
package main

import "fmt"

func main() {
    sum := 0
    for i := 1; i <= 100; i++ {
        sum += i
    }
    fmt.Printf("Sum 1..100 = %d\n", sum)
}

Next: a cleaner way to branch many ways — switch.