Go by Example

Range over Channels

Iterate over channel values until the channel is closed using a for-range loop.

A for range loop over a channel receives values until the channel is closed. This is the idiomatic way to consume a stream of values produced by a goroutine.

Launch a producer goroutine that sends values and then closes the channel. The consumer iterates with for v := range ch - the loop exits automatically when the channel closes.

package main
 
import "fmt"
 
func produce(ch chan<- int) {
    for i := 1; i <= 5; i++ {
        ch <- i
    }
    close(ch) // signals the range loop to stop
}
 
func main() {
    ch := make(chan int)
    go produce(ch)
 
    for v := range ch {
        fmt.Println(v)
    }
    fmt.Println("done")
}

Range over channels composes naturally into pipeline patterns, where one goroutine's output channel feeds the next stage's input:

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for v := range in {
            out <- v * v
        }
        close(out) // propagate the close downstream
    }()
    return out
}
 
nums := make(chan int)
go func() {
    for _, n := range []int{2, 3, 4} {
        nums <- n
    }
    close(nums)
}()
 
for sq := range square(nums) {
    fmt.Println(sq) // 4, 9, 16
}

In production

Range over a channel blocks until the channel is closed. If the sender never closes, the loop runs forever - a common goroutine leak. Always ensure the sending goroutine closes the channel, or give the loop an exit condition via a done channel or context.Context cancellation.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free