break, continue and Labels

break leaves a loop early and continue jumps to its next pass. On their own they affect only the innermost loop (or switch/select), so Go lets you attach a label to an outer loop and break out of, or continue, that one instead. This lesson covers both statements, labels, and Go's rarely needed goto.

break

break ends the innermost for, switch or select immediately. A typical use is stopping a search as soon as you've found what you want:

GO
package main

import "fmt"

func main() {
	orders := []int{120, 45, 980, 60, 1500}
	for i, amount := range orders {
		if amount > 500 {
			fmt.Printf("first large order: #%d (%d)\n", i, amount)
			break
		}
	}
}
Output
first large order: #2 (980)

continue

continue skips the rest of the current pass. It's a neat way to filter out items at the top of a loop body, instead of wrapping everything in an if:

GO
package main

import (
	"fmt"
	"strings"
)

func main() {
	lines := []string{"name=Ana", "# a comment", "", "city=Porto", "  "}
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		fmt.Println("setting:", line)
	}
}
Output
setting: name=Ana
setting: city=Porto

Labels for nested loops

In nested loops, a plain break leaves only the inner one. To leave both, put a label (a name followed by a colon) before the outer loop and write break label:

GO
package main

import "fmt"

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

search:
	for r, row := range grid {
		for c, v := range row {
			if v < 0 {
				fmt.Printf("negative value %d at row %d, column %d\n", v, r, c)
				break search
			}
		}
	}
	fmt.Println("search over")
}
Output
negative value -7 at row 1, column 2
search over

continue label works the same way, moving straight on to the next pass of the labelled loop:

GO
package main

import "fmt"

func main() {
	words := []string{"sky", "tree", "rhythm", "apple"}

nextWord:
	for _, w := range words {
		for _, r := range w {
			switch r {
			case 'a', 'e', 'i', 'o', 'u':
				continue nextWord // has a vowel: skip this word
			}
		}
		fmt.Println("no vowels:", w)
	}
}
Output
no vowels: sky
no vowels: rhythm

Labels are conventionally written in lowerCamelCase or ALLCAPS and placed on their own line, at a lower indent than the loop. An unused label is a compile error, just like an unused variable.

break inside switch or select in a loop

This is the most common reason to need a label. Inside a switch (or select, lesson 53), a plain break leaves the switch, not the surrounding loop:

GO
package main

import "fmt"

func main() {
	commands := []string{"add", "add", "quit", "add"}
	count := 0

loop:
	for _, cmd := range commands {
		switch cmd {
		case "add":
			count++
		case "quit":
			break loop // a plain break would only leave the switch
		}
	}
	fmt.Println("added", count, "times")
}
Output
added 2 times

goto

Go does have goto, which jumps to a label in the same function. It can't jump over variable declarations or into a block, and it's almost never the clearest option — you'll mainly meet it in generated code and a few low-level standard library functions. Prefer loops, labelled break/continue, and small functions with early returns.

Try it yourself

Find the first pair of numbers in the list that adds up to the target, then stop both loops.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
5 + 7 = 12
Next: how strings, bytes and runes work.