What a nested loop is
A nested loop is a loop inside another loop. For every pass of the outer loop, the inner loop starts again from its initial value and runs to completion. If the outer loop runs 5 times and the inner loop 10 times, the inner body runs 5 × 10 = 50 times.
Go has a single loop keyword, for, so every nested loop is made of for statements. The three-part form is init; condition; post, with no parentheses and with braces always required.
Printing a rectangle
The classic use is a grid. The outer loop counts rows and the inner loop counts columns:
for row := 0; row < 3; row++ {
for col := 0; col < 5; col++ {
fmt.Print("*")
}
fmt.Println()
}
*****
*****
*****
fmt.Print writes without a newline, so the five stars of a row stay together. fmt.Println() with no arguments ends the line. It sits after the inner loop's closing brace but inside the outer loop: placed inside the inner loop, it would put every star on its own line.
Walking a slice of slices
Nested loops also visit every element of a two-dimensional slice. Here arr[i][j] is element j of row i:
arr := [][]int{
{1, 2},
{3, 4},
{5, 6},
}
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Println(arr[i][j])
}
}
This prints 1 to 6, one per line. The trailing comma after {5, 6} is required because the closing brace is on its own line. Using len(arr[i]) for the inner bound keeps the loop correct when rows have different lengths. The range form is shorter when the indices are not needed:
for _, row := range arr {
for _, v := range row {
fmt.Print(v, " ")
}
fmt.Println()
}
1 2
3 4
5 6
break in nested loops
A break leaves only the loop it is written in. In the inner loop it ends that row's scan, and the outer loop carries on with the next row:
for i := 0; i < 3; i++ {
for j := 0; j < 2; j++ {
if arr[i][j] == 3 {
break
}
fmt.Println(arr[i][j])
}
}
1
2
5
6
At i = 1, j = 0 the value is 3, so the inner loop stops. The outer loop moves to i = 2 and prints all of row 2. The numbers 3 and 4 never appear.
A break in the outer loop, before the inner loop starts, stops everything:
for i := 0; i < 3; i++ {
if arr[i][0] == 3 {
break
}
for j := 0; j < 2; j++ {
fmt.Println(arr[i][j])
}
}
1
2
Leaving both loops with a label
To stop both loops from inside the inner one, Go lets you put a label before the outer for and name it in break:
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 2; j++ {
if arr[i][j] == 3 {
break outer
}
fmt.Println(arr[i][j])
}
}
This prints only 1 and 2. A continue can use a label the same way, to jump to the next pass of the outer loop.
Common mistakes
- Reusing the loop variable. Give each loop its own name, such as
rowandcol. Reusing the same name shadows the outer variable and breaks the logic. - Putting the newline in the wrong place.
fmt.Println()belongs after the inner loop. - Off-by-one bounds.
i < len(arr)is correct;i <= len(arr)panics with an index out of range error on the last pass. - Expecting a plain
breakto leave both loops. It does not; use a label or a flag.
The exercise, step by step
The task is a rectangle of stars with 5 rows and 10 columns.
- Write the outer loop with
rowstarting at 0 and running whilerow < 5: one pass per row. - Write the inner loop inside it with
colfrom 0 whilecol < 10: one pass per column. Its body runs 50 times in total. - Print one star with
fmt.Print("*")inside the inner loop. - End the row with
fmt.Println()after the inner loop, still inside the outer one.
for row := 0; row < 5; row++ {
for col := 0; col < 10; col++ {
fmt.Print("*")
}
fmt.Println()
}
The output is five lines of ten stars each.
Key takeaways
- The inner loop restarts and finishes completely on every pass of the outer loop.
- The body of two nested loops over
nandmitems runsn * mtimes. breakaffects only the innermost loop; a labeledbreakleaves an outer one.- Rows are the outer loop and columns the inner loop when printing or walking a grid.