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
- Q215Can you compare channels with
==, use them as map keys, and send channels over channels? - Q216How do you gracefully shut down a service that uses channels and goroutines?
- Q218What happens to goroutines that are already blocked on a channel when it is closed?
- Q219Go has no unbounded channels. Why not, and how would you build one?
- Q220What exactly gets copied when you send on a channel, and what limits apply to element types and buffer sizes?