for Loops & range

Go · Lesson 11 of 32 6 min read Updated

Go has only one loop keyword: for. There is no while and no do-while. Instead, for comes in a few shapes that cover every kind of repetition, and the range form walks through numbers, slices, strings, maps and more.

In this lesson you will write counting loops, condition loops and endless loops, control them with break, continue and labels, and use range the modern way.

The three-part for loop

The classic form has three parts separated by semicolons: an init statement that runs once, a condition checked before each round, and a post statement that runs after each round. As with if, there are no brackets around them.

Go
package main

import "fmt"

func main() {
	for i := 1; i <= 5; i++ {
		fmt.Println("7 x", i, "=", 7*i)
	}
}
Output
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35

The variable i exists only inside the loop. Since Go 1.22, each round of the loop gets a fresh copy of i, which fixes an old bug with closures and goroutines that you can read about in Old vs New Go.

Condition-only and endless loops

Leave out the init and post parts and for behaves like a while loop in other languages. Leave out everything and it loops forever, until a break or return stops it.

Go
package main

import "fmt"

func main() {
	// while-style: Rohan saves ₹1,500 a month for a ₹10,000 phone
	saved, months := 0, 0
	for saved < 10000 {
		saved += 1500
		months++
	}
	fmt.Println("Months:", months, "Saved:", saved)

	// endless loop with break: retry a connection
	attempt := 0
	for {
		attempt++
		if attempt == 3 {
			fmt.Println("Connected on attempt", attempt)
			break
		}
		fmt.Println("Attempt", attempt, "failed, retrying")
	}
}
Output
Months: 7 Saved: 10500
Attempt 1 failed, retrying
Attempt 2 failed, retrying
Connected on attempt 3

After six months Rohan has ₹9,000, which is still short, so the loop runs a seventh time. In the endless loop, break leaves the loop immediately. Real servers often run a for { } loop for their whole life, waiting for work.

range over numbers and slices

The range keyword makes a loop over a collection. Since Go 1.22 you can range over a plain integer, which counts from 0 up to one less than that number:

Go
package main

import "fmt"

func main() {
	for i := range 3 {
		fmt.Println("Row", i)
	}

	runs := []int{4, 12, 0, 9}
	total := 0
	for over, r := range runs {
		fmt.Printf("Over %d: %d runs\n", over+1, r)
		total += r
	}
	fmt.Println("Total:", total)
}
Output
Row 0
Row 1
Row 2
Over 1: 4 runs
Over 2: 12 runs
Over 3: 0 runs
Over 4: 9 runs
Total: 25

On a slice, range gives two values each round: the index and a copy of the element. If you need only the value, ignore the index with the blank identifier: for _, r := range runs. If you need only the index, write for i := range runs.

range over strings

Ranging over a string gives the byte position and the rune (Unicode character) at that position. Characters outside English take more than one byte, so the positions can jump:

Go
package main

import "fmt"

func main() {
	for i, r := range "Go₹5" {
		fmt.Println(i, string(r))
	}
}
Output
0 G
1 o
2 ₹
5 5

The rupee sign takes three bytes (positions 2, 3 and 4), so the next character starts at 5. You will see why in Strings, Bytes & Runes. You can also range over maps, channels and functions; those come in their own lessons. One warning now: ranging over a map gives the keys in a random order that changes between runs.

break, continue and labels

continue skips the rest of the current round and moves to the next one. break leaves the loop. Here a canteen counter skips blank orders and stops at "close":

Go
package main

import "fmt"

func main() {
	orders := []string{"tea", "", "dosa", "close", "coffee"}

	for _, item := range orders {
		if item == "" {
			continue
		}
		if item == "close" {
			break
		}
		fmt.Println("Serving", item)
	}
}
Output
Serving tea
Serving dosa

In nested loops, a plain break leaves only the innermost loop. To leave an outer loop, put a label (a name followed by a colon) before it and write break label:

Go
package main

import "fmt"

func main() {
	seats := [][]string{
		{"X", "X", "X"},
		{"X", "", "X"},
		{"", "", ""},
	}

search:
	for row := range seats {
		for col := range seats[row] {
			if seats[row][col] == "" {
				fmt.Println("First free seat: row", row+1, "seat", col+1)
				break search
			}
		}
	}
}
Output
First free seat: row 2 seat 2

[][]string is a slice of slices, a simple grid. Without the label, the outer loop would continue and print more free seats. continue search also works, to skip straight to the next outer round.

Common mistakes

  • Changing the copy instead of the slice. In for _, p := range prices { p = p * 2 }, only the copy p changes. Write prices[i] *= 2 using the index.
  • Expecting map order. range over a map is random; sort the keys first if order matters (see Maps).
  • Using break inside a switch in a loop. It leaves only the switch; use a labelled break to leave the loop.
  • Endless loops by accident. In a condition loop, make sure something inside changes the condition.

Key points

  • for is Go's only loop: three-part, condition-only and endless forms.
  • range gives index and value for slices, and byte position and rune for strings.
  • for i := range n counts from 0 to n-1 (Go 1.22 and later).
  • continue skips to the next round; break exits; labels control outer loops.
  • Each loop round has its own copy of the loop variables since Go 1.22.

Exercise

A Kochi fruit seller records daily sales for a week: 1200, 950, 0, 1430, 870, 0, 2100 rupees (0 means the shop was closed). Loop over the sales with range, skip closed days with continue, print each open day as "Day N: ₹amount", and print the total and the number of open days at the end.

Show solution
Go
package main

import "fmt"

func main() {
	sales := []int{1200, 950, 0, 1430, 870, 0, 2100}
	total, open := 0, 0

	for i, amount := range sales {
		if amount == 0 {
			continue
		}
		fmt.Printf("Day %d: ₹%d\n", i+1, amount)
		total += amount
		open++
	}
	fmt.Println("Total:", total, "Open days:", open)
}
Output
Day 1: ₹1200
Day 2: ₹950
Day 4: ₹1430
Day 5: ₹870
Day 7: ₹2100
Total: 6550 Open days: 5

continue skips days 3 and 6 before anything is printed or added. The index starts at 0, so i+1 gives human-friendly day numbers.

Quick Quiz

1. How do you write a while-style loop in Go?

2. What does for i := range 4 produce for i?

3. In nested loops, how do you leave the outer loop from inside the inner one?

4. In what order does range visit the keys of a map?

Learn Go with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call