What this lesson is about
A Go program runs from the first line to the last, one statement at a time. This order is called the execution flow. A loop changes it: the statements inside its braces run again and again, once for each element, and then the program carries on with the line after the closing brace.
The key question for every line near a loop is simple: is it inside the braces or outside them? Lines inside run once per element. Lines outside run once.
Inside and outside the braces
Go uses braces ({}) to mark the body of a loop. Here both calls to fmt.Println are in the body:
languages := []string{"Python", "Java", "JavaScript"}
for _, language := range languages {
fmt.Println("I love " + language)
fmt.Println("I want to get better at it")
}
The loop has three elements, so the body runs three times and prints six lines, two for each language:
I love Python
I want to get better at it
I love Java
I want to get better at it
I love JavaScript
I want to get better at it
If the second call moves below the closing brace, it is no longer part of the loop:
languages := []string{"Python", "Java", "JavaScript"}
for _, language := range languages {
fmt.Println("I love " + language)
}
fmt.Println("I want to get better at them")
Now the loop prints one line per language, and the last line is printed once, after the loop has finished:
I love Python
I love Java
I love JavaScript
I want to get better at them
Indentation helps you read this, but in Go the braces decide. The compiler does not care how a line is indented.
Reading the flow top to bottom
Take this program and follow it line by line:
fruits := []string{"banana", "apple", "kiwi"}
fmt.Println("I'm hungry!")
for _, fruit := range fruits {
fmt.Println("I'm gonna eat:")
fmt.Println(fruit)
}
fmt.Println("I love my life!")
First the slice is created. Then I'm hungry! is printed once. The loop runs its two lines for banana, then for apple, then for kiwi. When the elements run out, the flow moves to the last line, which prints once:
I'm hungry!
I'm gonna eat:
banana
I'm gonna eat:
apple
I'm gonna eat:
kiwi
I love my life!
The range loop in Go
The loop header for _, language := range languages has three parts:
range languageswalks over the slice from the first element to the last.- On each pass,
rangegives two values: the index of the element and the element itself. languagereceives the element on each pass. The blank identifier_receives the index and throws it away.
The _ is not decoration. Go refuses to compile a program with a variable that is declared and never used, so naming an index you do not need would be an error. Writing _ says that you are ignoring it on purpose.
Also note that the loop variable exists only inside the loop. A line after the closing brace cannot use language.
The exercise
The editor holds a slice of languages and two prints, one before the gap and one after it:
languages := []string{"Python", "Java", "JavaScript"}
fmt.Println("Today we'll learn about:")
// Write the for loop below:
fmt.Println("Let's code!")
The goal is to print the heading, then one language per line, then the closing message. Work through it in three decisions:
- The language names must come between the two existing prints, so the loop goes in the gap. That way the heading prints first and the closing message prints last.
- The loop needs to visit every element, so use
range languagesand discard the index with_. - The body is one line that prints the current element. The closing message stays outside, because it must appear once.
The finished program:
languages := []string{"Python", "Java", "JavaScript"}
fmt.Println("Today we'll learn about:")
// Write the for loop below:
for _, language := range languages {
fmt.Println(language)
}
fmt.Println("Let's code!")
Its output is the heading, the three names and the closing line:
Today we'll learn about:
Python
Java
JavaScript
Let's code!
Common mistakes
- Putting the closing print inside the braces. It then prints three times, once after each language.
- Putting the loop above the heading. The names then appear before the line that introduces them.
- Writing
for i, language := range languagesand never usingi. Go reportsdeclared and not used. - Using the loop variable after the closing brace. It is out of scope there.
Key takeaways
- Statements run from top to bottom, and a loop repeats the lines in its braces.
- Lines inside the braces run once per element. Lines outside run once.
rangegives the index and the element, and_discards what you do not need.- To check what a program prints, trace it line by line and count how many times each line runs.