A closed channel tells receivers that no more messages will arrive. Calling close(channel) prevents further sends; receiving afterward returns the data type’s default value, such as 0 for int. section 7.1.4
Handle closure explicitly rather than mistaking a valid default value for an end-of-stream signal:
- Read the value and open-channel flag: msg, more := <-messages. The flag becomes false when the channel is closed and has no more messages. section 7.1.4
- Range over the channel with for msg := range messages; iteration stops automatically when the channel closes. section 7.1.4
- In select, remove a closed input by assigning its channel variable to nil; otherwise its case remains continuously ready and can cause needless looping. section 8.1.6
Receiving until closure
A sender emits 1, 2, and 3, then closes the channel. A receiver using for msg := range messages prints those values and finishes, unlike an infinite receive loop that keeps printing 0. section 7.1.4
Listing 8.12 A main() goroutine using the nil select pattern
func main() { sales := generateAmounts(50) #1 expenses := generateAmounts(40) #2 endOfDayAmount := 0 for sales != nil || expenses != nil { #3 select { case sale, moreData := <-sales: #4 if moreData { fmt.Println("Sale of:", sale) endOfDayAmount += sale #5 } else { sales = nil #6 } case expense, moreData := <-expenses: #7 if moreData { fmt.Println("Expense of:", expense) endOfDayAmount -= expense #8 } else { expenses = nil #9 } } } fmt.Println("End of day profit and loss:", endOfDayAmount) } #1 Generates 50 amounts on the sales channel #2 Generates 40 amounts on the expenses channel #3 Continues to loop while there is a non-nil channel #4 Consumes the next amount and channel open flag from the sales channel\ #5 Adds the sales amount to the total end-of-day balance #6 If the channel has been closed, marks the channel as nil, disabling this select case #7 Consumes the next amount and channel open flag from the expenses channel #8 Subtracts the expense amount from the total end-of-day balance #9 If the channel has been closed, marks the channel as nil, disabling this select case
Follow the sales-and-expenses example to see two generated channels consumed with select, disabled individually after closure, and combined into an end-of-day total. section 8.1.6

