for — The Only Loop

Go has one loop keyword. The same for covers counting, looping while a condition holds, and looping forever.

Syntaxfor init; condition; post { ... } for condition { ... } for { ... }

A loop runs the same code more than once. Go has a single loop keyword, for, and every kind of loop is written with it. There is no separate while or do keyword to learn.

Three clauses

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

The clauses are separated by semicolons. The first runs once, before the loop starts. The second is a test, checked before every pass; the loop ends as soon as it is false. The third runs after every pass, and is usually where a counter moves on. As with if, there are no brackets around the clauses and the braces are required.

Leaving clauses out

The first and third clauses are optional. Keep only the test and you get a loop driven by a condition. These two loops do exactly the same thing:

// longhand: empty first and third clauses
for ; n < 100; {
	n = n * 2
}

// shorthand: drop the semicolons too
for n < 100 {
	n = n * 2
}

Drop the test as well and the loop never ends by itself: for { is the same as writing for true {.

Stopping and skipping

break ends the loop immediately. continue abandons the rest of the body and starts the next pass. In the three-clause form, continue still runs the third clause, so the counter keeps moving. A for with no test needs a break or a return somewhere inside it, or the program hangs.

Example

Example · go
package main

import "fmt"

func main() {
	// Three clauses: set up, test, advance.
	total := 0
	for i := 1; i &lt;= 5; i++ {
		total += i // same as total = total + i
	}
	fmt.Println("total:", total)

	// Test only. The loop stops when n is no longer below 20.
	n := 1
	for n &lt; 20 {
		n = n * 3
	}
	fmt.Println("n:", n)

	// No clauses at all, so break is the only way out.
	count := 0
	for {
		count++
		if count == 3 {
			break
		}
	}
	fmt.Println("count:", count)
}

// Output:
// total: 15
// n: 27
// count: 3

When to use it

  • Add up or count the items in a list of values.
  • Keep halving or doubling a number until it crosses a limit.
  • Read input line by line until the source runs out.
  • Retry a failing operation a fixed number of times, stopping on the first success.

More examples

Leaving two loops at once with a label

A plain break only ends the loop it sits in, which is the inner one here. Labelling the outer loop and writing break outer ends both. This is the standard way to abandon a search once you have found what you were looking for in a grid or a nested pair of lists.

Example · go
package main

import "fmt"

func main() {
	// A label is a name for a loop. break outer leaves the labelled
	// loop; a plain break would only leave the inner one.
outer:
	for i := 1; i &lt;= 3; i++ {
		for j := 1; j &lt;= 3; j++ {
			if i*j &gt; 4 {
				break outer
			}
			fmt.Println(i, j, i*j)
		}
	}
}

// Output:
// 1 1 1
// 1 2 2
// 1 3 3
// 2 1 2
// 2 2 4

A loop that stops when a function says so

The condition-only form shines when something else decides how many passes there are. Run this with input piped into it and it prints each line. The same shape appears whenever you read rows, records or messages until a reader reports that there are no more.

Example · go
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// bufio.Scanner reads one line at a time. Scan reports true while
	// there is another line, and false at the end or on an error.
	sc := bufio.NewScanner(os.Stdin)
	for sc.Scan() {
		fmt.Println("line:", sc.Text())
	}
	// Err reports the error, or nil if the input simply ended.
	if err := sc.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "read failed:", err)
		os.Exit(1)
	}
}

A retry loop with a counter

A counted loop with a break is the plain way to retry a fixed number of times. Note that err is declared before the loop, so its last value is still readable after the loop ends and the code can tell success from exhaustion.

Example · go
package main

import "fmt"

func send(attempt int) error {
	if attempt &lt; 3 {
		return fmt.Errorf("attempt %d refused", attempt)
	}
	return nil
}

func main() {
	const maxAttempts = 3
	var err error
	for attempt := 1; attempt &lt;= maxAttempts; attempt++ {
		err = send(attempt)
		if err == nil {
			fmt.Println("sent on attempt", attempt)
			break // stop as soon as it works
		}
		fmt.Println("failed:", err)
	}
	if err != nil {
		fmt.Println("gave up after", maxAttempts, "attempts")
	}
}

// Output:
// failed: attempt 1 refused
// failed: attempt 2 refused
// sent on attempt 3

Discussion

  • Be the first to comment on this lesson.