Looping with range

A for … range loop walks through the items of a collection: the elements of a slice or array, the characters of a string, the entries of a map, the values arriving on a channel, or the numbers below an integer. This lesson covers each kind, what the two loop variables mean in each case, and two classic surprises — copied values and random map order.

Slices and arrays

Ranging over a slice gives the index and a copy of the element at that index. Use _ for the one you don't need, or leave the second variable out to get only indexes:

GO
package main

import "fmt"

func main() {
	fruits := []string{"apple", "banana", "cherry"}

	for i, fruit := range fruits {
		fmt.Println(i, fruit)
	}
	for _, fruit := range fruits {
		fmt.Print(fruit, "; ")
	}
	fmt.Println()
	for i := range fruits {
		fmt.Print(i, " ")
	}
	fmt.Println()
}
Output
0 apple
1 banana
2 cherry
apple; banana; cherry;
0 1 2

The value is a copy

The element variable holds a copy. Changing it doesn't change the slice. To modify elements, assign through the index:

GO
package main

import "fmt"

func main() {
	prices := []float64{10, 20, 30}

	for _, p := range prices {
		p *= 2 // changes only the copy
	}
	fmt.Println(prices)

	for i := range prices {
		prices[i] *= 2
	}
	fmt.Println(prices)
}
Output
[10 20 30]
[20 40 60]

The first loop compiles and runs without complaint, which makes this an easy bug to miss. Remember the rule: to change elements, assign through the index.

Strings

Ranging over a string decodes UTF-8 and gives each character as a rune, together with the byte position where it starts. Characters outside plain ASCII take more than one byte, so the positions can jump:

GO
package main

import "fmt"

func main() {
	for i, r := range "Añob" {
		fmt.Printf("byte %d: %c (%U)\n", i, r, r)
	}
}
Output
byte 0: A (U+0041)
byte 1: ñ (U+00F1)
byte 3: o (U+006F)
byte 4: b (U+0062)

Indexing a string directly, as in s[1], gives a single byte instead. Lesson 19 explains the difference.

Maps

Ranging over a map gives each key and its value. The order is deliberately unpredictable and can change from one run to the next, so never rely on it. When you need a stable order, sort the keys first:

GO
package main

import (
	"fmt"
	"maps"
	"slices"
)

func main() {
	stock := map[string]int{"pears": 4, "apples": 12, "figs": 0, "kiwis": 7}

	total := 0
	for _, qty := range stock { // order doesn't matter for a sum
		total += qty
	}
	fmt.Println("total:", total)

	for _, fruit := range slices.Sorted(maps.Keys(stock)) {
		fmt.Printf("%-7s %2d\n", fruit, stock[fruit])
	}
}
Output
total: 23
apples  12
figs     0
kiwis    7
pears    4

maps.Keys produces the keys and slices.Sorted collects them into a sorted slice. Lessons 27 and 28 cover these packages.

Integers

As you saw in the previous lesson, range n counts from 0 to n−1:

GO
package main

import "fmt"

func main() {
	for i := range 3 {
		fmt.Print(i)
	}
	fmt.Println()
}
Output
012

Channels and functions

range also works on channels, receiving values until the channel is closed (lesson 52), and on iterator functions, which produce values one at a time (lesson 50). Here's a quick preview of both:

GO
package main

import (
	"fmt"
	"strings"
)

func main() {
	ch := make(chan string, 3)
	ch <- "a"
	ch <- "b"
	close(ch)
	for v := range ch {
		fmt.Print(v, " ")
	}
	fmt.Println()

	for word := range strings.FieldsSeq("one two  three") {
		fmt.Print("[", word, "]")
	}
	fmt.Println()
}
Output
a b
[one][two][three]

Summary

Ranging overFirst variableSecond variable
Slice or arrayindex (int)copy of the element
Stringbyte position (int)character (rune)
Mapkeyvalue
Channelreceived value—
Integer n0 to n−1—
Iterator functionwhatever it yieldswhatever it yields (for two-value iterators)

Try it yourself

Count how often each letter appears in a sentence, ignoring spaces, and print the counts in alphabetical order.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
a:4 b:2 d:1 e:1 n:2 r:1
Next: break, continue and labels.