Lesson 12 · 12 of 20
Channels · Go
Connect goroutines in Go with channels: send and receive, close a channel, loop over it with range and build a two-stage pipeline.

A channel is a typed pipe that goroutines use to pass values to each other. make(chan Line) creates one for order lines; out <- l sends and <-total receives. It is Go's way of coordinating: do not communicate by sharing memory; share memory by communicating.
This channel is unbuffered: every send waits until someone receives, and every receive waits until someone sends. So besides carrying data, the channel synchronises: produce cannot run ahead of its reader.
produce is a common pattern, the generator: it creates the channel, starts a goroutine that fills it and returns it straight away. The return type, <-chan Line, is a receive-only channel: whoever gets it can neither send nor close. When it is done, defer close(out) closes it to say no more values are coming.
The second stage loops over the channel with for l := range produce(lines), which receives until the channel is closed; without the close, the loop would wait forever. It prints each line, adds up the amount and sends the total on another channel. In main, <-total waits until that value arrives, with no WaitGroup needed.
With one sender and one receiver the order of values is kept and the output is always the same. Two rules: the sender closes the channel, never the receiver, and sending on a closed channel panics.
Code · main.go
package mainimport "fmt"type Line struct { Item string Qty int Price float64}func produce(lines []Line) <-chan Line { // sends every line, then closes the channel out := make(chan Line) // unbuffered: each send waits for a receiver go func() { defer close(out) for _, l := range lines { out <- l } }() return out}func main() { lines := []Line{{"Coffee", 2, 3.5}, {"Croissant", 3, 1.8}, {"Orange juice", 1, 2.9}} total := make(chan float64) go func() { // a second stage: receives the lines, sends back one total sum := 0.0 for l := range produce(lines) { // the loop ends when produce closes its channel fmt.Printf("%-13s %d x %.2f\n", l.Item, l.Qty, l.Price) sum += float64(l.Qty) * l.Price } total <- sum }() fmt.Printf("total: %.2f EUR\n", <-total) // main waits here until the total arrives}Output · go run main.go
Coffee 2 x 3.50 Croissant 3 x 1.80 Orange juice 1 x 2.90 total: 15.30 EUR
Key points
ch <- vsends;v := <-chreceives.- Unbuffered, each send waits for its receiver: the channel synchronises.
<-chan Tis a receive-only channel.close(ch)says there are no more values;rangestops there.- The sender closes; sending on a closed channel panics.
Exercise
Add a third stage between the two: a discount(in <-chan Line) <-chan Line function that takes 10% off the price of lines with a Qty of 3 or more, and chain it with range discount(produce(lines)).