for Loops & range
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.
package main
import "fmt"
func main() {
for i := 1; i <= 5; i++ {
fmt.Println("7 x", i, "=", 7*i)
}
}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.
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")
}
}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:
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)
}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:
package main
import "fmt"
func main() {
for i, r := range "Go₹5" {
fmt.Println(i, string(r))
}
}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":
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)
}
}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:
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
}
}
}
}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 copypchanges. Writeprices[i] *= 2using the index. - Expecting map order.
rangeover a map is random; sort the keys first if order matters (see Maps). - Using
breakinside aswitchin a loop. It leaves only theswitch; use a labelledbreakto leave the loop. - Endless loops by accident. In a condition loop, make sure something inside changes the condition.
Key points
foris Go's only loop: three-part, condition-only and endless forms.rangegives index and value for slices, and byte position and rune for strings.for i := range ncounts from 0 to n-1 (Go 1.22 and later).continueskips to the next round;breakexits; 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
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)
}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?
Go has no while keyword; a for with only a condition does the same job.
2. What does for i := range 4 produce for i?
Ranging over an integer n counts from 0 up to n-1.
3. In nested loops, how do you leave the outer loop from inside the inner one?
Go uses labelled break and continue to control outer loops.
4. In what order does range visit the keys of a map?
Map iteration order is deliberately not specified; sort the keys if you need an order.
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
