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:
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 } } }
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:
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) } }
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:
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") }
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:
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) } }
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:
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") }
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.
5 + 7 = 12