Channels
What is a channel?
Section titled “What is a channel?”A channel is a typed conduit through which goroutines communicate. Sending a value into a channel and receiving from it are the two operations that transfer both data and synchronization in a single step.
ch := make(chan int) // unbuffered channel of intch <- 42 // send (blocks until a receiver is ready)v := <-ch // receive (blocks until a sender is ready)The type system enforces what flows through a channel. A chan string will not accept an int.
Unbuffered vs buffered
Section titled “Unbuffered vs buffered”| Unbuffered | Buffered | |
|---|---|---|
| Created with | make(chan T) | make(chan T, capacity) |
| Send blocks? | Yes — until a receiver is ready | No — until the buffer is full |
| Receive blocks? | Yes — until a sender is ready | No — until the buffer is empty |
| Synchronizes? | Yes — sender and receiver meet at the same moment | No — the buffer decouples timing |
Unbuffered channels provide the strongest synchronization guarantee: the sender knows the receiver has the value before it continues.
Channel directions
Section titled “Channel directions”You can restrict a channel parameter to send-only or receive-only to encode the intent at the type level:
func producer(out chan<- int) { out <- 1 } // send-onlyfunc consumer(in <-chan int) { v := <-in; _ = v } // receive-onlyA bidirectional chan T is assignable to either direction. The compiler enforces that a receive-only channel cannot be sent to, and vice versa.
Closing a channel and ranging over it
Section titled “Closing a channel and ranging over it”The sender calls close(ch) to signal that no more values will be sent. Receivers can use a for range loop to drain all values until close:
close(ch)for v := range ch { // exits when ch is closed and empty fmt.Println(v)}The comma-ok receive idiom
Section titled “The comma-ok receive idiom”A receive expression can return a second boolean that tells you whether the channel is still open:
v, ok := <-ch// ok == true → v is a real value// ok == false → channel is closed and drained; v is the zero valuepackage main
import "fmt"
func producer(ch chan<- int, n int) { for i := 1; i <= n; i++ { ch <- i } close(ch)}
func main() { // Unbuffered channel: producer runs in a goroutine ch := make(chan int) go producer(ch, 5) for v := range ch { fmt.Println(v) }
// Buffered channel: all sends fit in the buffer buf := make(chan string, 3) buf <- "a" buf <- "b" buf <- "c" close(buf) for s := range buf { fmt.Println(s) }
// Comma-ok: distinguish real value from closed zero value done := make(chan bool, 1) done <- true close(done) v1, ok1 := <-done fmt.Println(v1, ok1) // true true v2, ok2 := <-done fmt.Println(v2, ok2) // false false}Loading Go runtime (first run only, ~8 MB)…