The best way of handling the end of a synchronization between a sender and a receiver is the close operation. This function is normally executed by the sender because the receiver can verify whether the channel is still open each time it gets a value using a second variable:
value, ok := <-ch
The second receiver is a Boolean that will be true if the channel is still open, and false otherwise. When a receive operation is done on a close channel, the second received variable will have the false value, and the first one will have the 0 value of the channel type, such as these:
- 0 for numbers
- false for Booleans
- "" for strings
- nil for slices, maps, or pointers
The example of sending multiple messages can be rewritten using the close function, without having prior knowledge of how many messages will be sent:
func main() {
const max = 10
var a = make(chan int)
go func() {
for i := 0; i < max; i++ {
a <- i
}
close(a)
}()
for {
v, ok := <-a
if !ok {
break
}
fmt.Println(v)
}
}
The full example is available at https://play.golang.org/p/GUzgG4kf5ta.
There is a more synthetic and elegant way to receive a message from a channel until it's closed: by using the same keyword that we used to iterate maps, arrays, and slices. This is done through range:
for v := range a {
fmt.Println(v)
}