The for loop and range
Go has exactly one loop keyword, for. To visit every element of a slice, an array, a string or a map, you combine it with range:
fruits := []string{"banana", "orange", "pear", "kiwi"}
for _, fruit := range fruits {
fmt.Println("I eat " + fruit)
}
Each part has a job:
forstarts the loop.range fruitssays which collection to walk through, from the first element to the last._, fruitare the two loop variables. On every pass, the first holds the index and the second holds the element at that index.- The braces hold the body, which runs once per element.
The output is:
I eat banana
I eat orange
I eat pear
I eat kiwi
Why the underscore
range always offers both the index and the value. Go does not allow a declared variable to go unused, so a loop variable you ignore must be replaced by the blank identifier _. That is why the loops here start with _,. If you want only the index, you can leave the second variable out:
for i := range fruits {
fmt.Println(i) // 0, 1, 2, 3
}
If you want both, name both: for i, fruit := range fruits.
Looping through a string
A string can be ranged over too. Each pass gives the byte position of a character and the character itself, as a rune, which is an integer holding a Unicode code point:
name := "Andy"
for _, letter := range name {
fmt.Println(string(letter))
}
This prints A, n, d and y on separate lines. The conversion string(letter) turns the rune back into a one-character string. Without it, fmt.Println(letter) would print the number 65 for A. Indexing a string with name[0] is different: it gives a single byte, not a character. With range, characters outside ASCII still come out whole. Ranging over "héy" would yield three runes, even though the string is four bytes long.
The index-based alternative
Go also has the three-part loop, which counts with an index you manage yourself:
for i := 0; i < len(fruits); i++ {
fmt.Println(fruits[i])
}
It prints the same fruits, but it gives you more places to slip: start at the wrong number, use <= instead of < and read one element past the end, which panics. When you only need the elements, range is shorter and safer, so it is the usual choice.
The exercise, step by step
The editor starts with a slice named friends holding Andy, Mary and John. The goal is to greet each one.
- Write the loop header. Use
for _, friend := range friends. The variable is namedfriend, and the index goes to_because the greeting doesn't need it. - Print the greeting in the body. Build the text with
"Hey, " + friend + "!"and pass it tofmt.Println, which adds the line break. The body runs once per friend, so each name gets its own line.
The solution in Go
friends := []string{"Andy", "Mary", "John"}
for _, friend := range friends {
fmt.Println("Hey, " + friend + "!")
}
The output is:
Hey, Andy!
Hey, Mary!
Hey, John!
The loop variable friend is a copy of the element for that pass. Changing it inside the body does not change the slice. An empty slice runs the body zero times and prints nothing.
A slice that is empty or nil is fine too. The loop checks for elements before each pass and simply never enters the body, so no special case is needed in your code.
Common mistakes
- Forgetting the underscore.
for i, friend := range friendswithinever used fails withi declared and not used. - Putting the variable in quotes.
"Hey, friend!"prints the wordfriendevery time. Join the variable to the text with+. - Printing outside the braces. A statement after the closing brace runs once, after the loop, and cannot see the loop variable.
- Mixing up the order. The index always comes first and the value second.
Key takeaways
for index, value := range collectionvisits every element in order.- Use
_for a loop variable you don't need, since unused variables are compile errors. - Ranging over a string gives runes, which you can turn back into text with
string(r).