Range over channels in Go

It’s common to see range clause with array, slice, string or map as an expression’s type:

m := make(map[string]float64)
m["oneone"] = 1.1
m["twotwo"] = 2.2
for key, value := range m {
fmt.Printf("[%s]: %.1f\n", key, value)
}
a := [3]int{3, 2, 1}
for idx, value := range a {
fmt.Printf("[%d]: %d\n", idx, value)
}
s := []int{30, 20, 10}
for idx, value := range s {
fmt.Printf("[%d]: %d\n", idx, value)
}
name := "Michał"
for idx, code := range name {
fmt.Printf("[%d]: %q\n", idx, code)
}

output:

[oneone]: 1.1
[twotwo]: 2.2
[0]: 3
[1]: 2
[2]: 1
[0]: 30
[1]: 20
[2]: 10
[0]: 'M'
[1]: 'i'
[2]: 'c'
[3]: 'h'
[4]: 'a'
[5]: 'ł'

What is also possible is to iterate through values sent over a channel. To break such iteration channel needs to be closed explicitly. Otherwise range would block forever in the same way as for nil channel. Let’s see an example:

package main
import "fmt"
func FibonacciProducer(ch chan int, count int) {
n2, n1 := 0, 1
    for count >= 0 {
ch <- n2
count--
n2, n1 = n1, n2+n1
}
close(ch)
}
func main() {
ch := make(chan int)
go FibonacciProducer(ch, 10)
idx := 0
for num := range ch {
fmt.Printf("F(%d): \t%d\n", idx, num)
idx++
}
}

output:

F(0): 0
F(1): 1
F(2): 1
F(3): 2
F(4): 3
F(5): 5
F(6): 8
F(7): 13
F(8): 21
F(9): 34
F(10): 55

Index variable is not allowed when range expression is a channel (source code):

ch := make(chan int)
go FibonacciProducer(ch, 10)
for idx, num := range ch {
fmt.Printf("idx: %d F(%d): \t%d\n", idx, num)
}

as it gives too many variables in range error while compilation. Spec says about such case explicitly (here):

If the range expression is a channel, at most one iteration variable is permitted, otherwise there may be up to two.

👏👏👏 below to help others discover this story. Please follow me here or on Twitter if you want to get updates about new posts or boost work on future stories.

Resources