The bug
The program should greet once and then list three foods:
I'm hungry!
I'll eat some pasta
I'll eat some burgers
I'll eat some pizza
This is the code we start from:
foods := []string{"pasta", "burgers", "pizza"}
for _, food := range foods {
fmt.Println("I'm hungry!")
fmt.Println("I'll eat some " + food)
}
Run it and I'm hungry! shows up before every food:
I'm hungry!
I'll eat some pasta
I'm hungry!
I'll eat some burgers
I'm hungry!
I'll eat some pizza
Why it happens
Everything between the braces of a for loop is the loop body, and the body runs once for each element of the slice. There are three foods, so each statement in the braces runs three times.
The greeting is inside the braces, so it repeats with the foods. The other line should repeat, because it uses the loop variable food and needs a fresh value each time. The greeting does not use food at all. That is a good test: a statement that does not depend on the loop variable and should appear only once probably belongs outside the loop.
The fix
Move the greeting above the loop. Go runs the program from top to bottom, so the greeting prints first, one time, and then the loop prints the three foods:
foods := []string{"pasta", "burgers", "pizza"}
fmt.Println("I'm hungry!")
for _, food := range foods {
fmt.Println("I'll eat some " + food)
}
The position matters in both directions. If you moved the greeting below the closing brace instead, it would print once, but at the end, after the foods, which is the wrong order.
Details worth knowing
- The braces decide what is in the loop. Indentation does not, although gofmt-style indentation makes the structure easy to see.
for _, food := range foodsgives the index and the element on each pass. The index goes to_because Go does not allow an unused variable.- The text
"I'll eat some " + foodjoins two strings. Note the space at the end of the first one, which keeps the words apart.
How to debug this kind of problem
Count how many times each line should run. Here the expected output has one greeting and three food lines, so the greeting runs once and the other line runs three times. A line that runs the wrong number of times is in the wrong place: either inside a loop that it should not be in, or outside one that it should be in.
When you fix a loop like this, run the program again and compare the output line by line with the expected one. Fixes often work for the count but break the order, so check both.
Tracing both versions
Tracing means following the program by hand and writing down what it prints. For the buggy version, the loop starts with food set to pasta: the greeting prints, then the pasta line. The loop then moves to burgers and repeats both lines, and again for pizza. That is six lines in total, two per pass.
For the fixed version, the greeting prints first and the loop is not running yet. Then each pass prints one line, so the three passes print three lines. The total is four lines, which matches the expected output.
What if the slice changes?
Add a fourth food to the slice and the fixed program still greets once and lists four foods, with no other change. The buggy version would greet four times. Keeping the once-only lines outside the loop makes the program work for any number of elements, including an empty slice, where the fixed program prints only the greeting.