Lesson 9 / 25
The nil-Channel Trick
Disabling select cases dynamically.
A nil case never fires
Because operations on a nil channel block forever, a select case on a nil channel can never be chosen. You can therefore switch a case off by setting the channel variable to nil. Classic use: merging two input channels. When one input is closed (ok == false), set it to nil so the select stops spinning on its endless zero values, and exit the loop once both are nil. The same trick enables a send case only when there is something to send: keep the outgoing channel nil while your pending queue is empty.
Merging two channels until both are closed
Closed inputs are disabled by setting them to nil.
package main
import "fmt"
func merge(a, b <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for a != nil || b != nil {
select {
case v, ok := <-a:
if !ok {
a = nil // disable this case
continue
}
out <- v
case v, ok := <-b:
if !ok {
b = nil
continue
}
out <- v
}
}
}()
return out
}
func gen(vals ...int) <-chan int {
ch := make(chan int)
go func() {
defer close(ch)
for _, v := range vals {
ch <- v
}
}()
return ch
}
func main() {
for v := range merge(gen(1, 2), gen(10, 20, 30)) {
fmt.Println(v) // all five values, interleaving not guaranteed
}
}Unplugging a phone line
A switchboard operator listens to many lines. When a caller hangs up for good, the operator unplugs that line so its dial tone stops demanding attention; the other lines keep working.
Quick check: Why set a closed input channel variable to nil inside a merge loop?
- It frees the channel memory immediately
- Setting it to nil reopens the channel
- A closed channel is always ready and returns zero values, while a nil channel case is never selected
- select panics on closed channels
Answer
A closed channel is always ready and returns zero values, while a nil channel case is never selected — Without the nil assignment the loop would spin on zero values from the closed channel.