Go

What does this print? (break inside select inside for)

Question 217HardGo 1.22 to 1.25
package main

import "fmt"

func main() {
	ch := make(chan int)
	go func() {
		for i := range 3 {
			ch <- i
		}
		close(ch)
	}()

	for {
		select {
		case v, ok := <-ch:
			if !ok {
				break
			}
			fmt.Println(v)
		}
	}
	fmt.Println("done")
}

It prints 0, 1, 2 and then hangs forever at 100% CPU. "done" is never printed. An unlabeled break exits the innermost for, switch or select, which here is the select, not the loop. After the close, every iteration receives ok == false right away, breaks out of the select, and loops again. The runtime doesn't report a deadlock because the goroutine is running, not blocked. The program compiles; go vet flags the final Println as unreachable.

Fix it with a labeled break (or return):

loop:
	for {
		select {
		case v, ok := <-ch:
			if !ok {
				break loop
			}
			fmt.Println(v)
		}
	}
	fmt.Println("done")

A select with a single case is just a plain receive, so for v := range ch is simpler here. The labeled form matters once you add a ctx.Done() or timeout case.

More on Channels & select

All 38 Channels & select questions