Skip to content
gohacks.dev
Basics

Labels Break and Continue in Nested Loops in Go Code

A labeled break or continue reaches an outer loop from inside an inner loop, switch or select. See the syntax and when a function is cleaner.

3 min read
Summarize in AI
Flat illustration of an editor window full of code.

You are searching a grid for a value. You found it three loops deep and you want to stop everything, and the break you wrote stopped one loop. The Go answer is a label, the small piece of syntax that most programs use zero or one times. It is worth knowing exactly, because the same trap appears inside switch and select, which are covered in the switch article and select in Go. This tip is part of the Go basics path, and it pairs with for range.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

Break and continue with a label

Write the label on its own line before the statement it names, followed by a colon. Then break or continue can name it:

main.goGo
package main

import "fmt"

func main() {
	grid := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}

search:
	for r, row := range grid {
		for c, v := range row {
			if v == 5 {
				fmt.Println("found 5 at", r, c)
				break search
			}
		}
	}

rows:
	for _, row := range grid {
		for _, v := range row {
			if v%3 == 0 {
				continue rows
			}
			fmt.Print(v, " ")
		}
	}
	fmt.Println()
}
Text
found 5 at 1 1
1 2 4 5 7 8

break search ended both loops at the first match. continue rows is the less familiar form: when the inner loop meets a multiple of three, it abandons the rest of that row and the outer loop advances, so the output skips everything after each row's first multiple of three. Without the label, continue would only skip the single value. Labels live in their own namespace, so a label named rows does not clash with a variable of that name.

The rule that catches typos

A label that nothing uses is an error:

main.goGo
package main

import "fmt"

func main() {
outer:
	for i := 0; i < 3; i++ {
		fmt.Println(i)
	}
}
Text
# example.test/lab
./main.go:6:1: label outer defined and not used

The message is label outer defined and not used. It is the same strictness as unused variables and imports, and it means a refactor that removes the last break outer forces you to remove the label as well. A label must also be attached to a for, switch or select for break and continue to refer to it, and continue needs a for.

A switch or select inside a loop

The most common reason to need a label has nothing to do with nested loops. It is a switch or select inside a loop where you want to leave the loop. A bare break there only leaves the switch, and the loop carries on, which has caused infinite loops in worker code that tried to stop on a "done" case:

main.goGo
package main

import "fmt"

func main() {
	events := make(chan string, 4)
	for _, e := range []string{"tick", "tick", "stop", "tick"} {
		events <- e
	}
	close(events)

loop:
	for e := range events {
		switch e {
		case "stop":
			fmt.Println("stopping")
			break loop
		default:
			fmt.Println("handling", e)
		}
	}
}
Text
handling tick
handling tick
stopping

break loop leaves the for, and the last tick stays unread in the channel. A plain break in that stop case would have printed stopping and then handled the final tick.

When a function is cleaner

If the labeled loop sits in a function that has nothing else to do after the search, return from it. A return from the middle of nested loops says the same thing without a label, and the function name documents what was being searched for:

Go
func find(grid [][]int, target int) (r, c int, ok bool) {
	for r, row := range grid {
		for c, v := range row {
			if v == target {
				return r, c, true
			}
		}
	}
	return 0, 0, false
}

I use a label when the code after the loops belongs to the same function, and a function when it does not. If you find yourself with two or three labels in one function, that function is doing too much, and splitting it up will help more than any label name. The rules are in the specification on labeled statements and on break statements.

Taggedquick tip
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.