How do labeled break and continue work? Why does a plain break inside select not leave the loop?
Question 22MediumGo 1.22 to 1.25
An unlabeled break ends the innermost for, switch or select. Inside a select or switch that sits in a loop, a plain break leaves only the select or switch, and the loop keeps going. This is a very common bug. A label on the loop fixes it.
func consume(ctx context.Context, ch <-chan int) {
loop:
for {
select {
case v, ok := <-ch:
if !ok {
break loop // leaves the for loop
}
fmt.Println(v)
case <-ctx.Done():
break loop
}
}
fmt.Println("done")
}
// continue with a label: skip to the next iteration of the OUTER loop
outer:
for i := range 3 {
for j := range 3 {
if j == 1 {
continue outer
}
fmt.Println(i, j) // prints (0,0) (1,0) (2,0)
}
}
Rules:
- A
breaklabel must name an enclosingfor,switchorselect. - A
continuelabel must name an enclosingfor. - An unused label is a compile error.
- Labels have function scope and don't clash with variable names.
An alternative that is often cleaner: move the loop into a function and use return.
More on Language Fundamentals & Types
- Q20In what order are package-level variables initialized? What does this print?
- Q21How does initialization work across packages, and what are the rules for
init()? - Q23What restrictions does Go place on
goto? - Q24How does
switchdiffer from C, and what does thisfallthroughexample print? - Q25Which types are comparable in Go, and when does comparing values panic at run time?
- Q26How does struct and array equality work? What is surprising about NaN as a map key?