Go

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 break label must name an enclosing for, switch or select.
  • A continue label must name an enclosing for.
  • 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

All 39 Language Fundamentals & Types questions