Explain channels — unbuffered vs. buffered — and how they synchronize goroutines.
Quick Answer
A channel (chan T) is a typed pipe for sending and receiving values between goroutines, created with make(chan T) (unbuffered) or make(chan T, n) (buffered with capacity n). An unbuffered channel makes both the sender and receiver block until the other side is ready — the send only completes once a receive is happening simultaneously, which is why unbuffered channels double as a synchronization point, not just a data pipe. A buffered channel lets a sender proceed without a waiting receiver, as long as the buffer isn't full; once full, the sender blocks until space opens up. Closing a channel (close(ch)) signals no more values will be sent, and receivers can detect this with the two-value receive form, v, ok := <-ch.
Detailed Answer
Channels are how goroutines talk to each other safely, following Go's philosophy: share memory by communicating, not by locking.
Unbuffered: a rendezvous point
ch := make(chan int)
go func() {
ch <- 42 // blocks until someone receives
}()
v := <-ch // blocks until someone sends
fmt.Println(v) // 42
The send and receive happen at the same logical moment. This makes an unbuffered channel useful as a synchronization signal, not just a value carrier — receiving from it tells you the sender has reached that specific point.
Buffered: decoupled, up to a point
ch := make(chan int, 2)
ch <- 1 // doesn't block, buffer has room
ch <- 2 // doesn't block, buffer now full
ch <- 3 // blocks — buffer is full, waits for a receive
Closing and detecting closure
ch := make(chan int, 3)
ch <- 1
ch <- 2
close(ch)
v, ok := <-ch // 1, true
v, ok = <-ch // 2, true
v, ok = <-ch // 0, false — channel closed and drained
for v := range ch also stops automatically once a channel is closed and drained, making it the idiomatic way to consume every value from a channel until its producer is done.
A key rule
Only the sender should close a channel, never the receiver, and never close a channel twice — both cause a panic. Sending on a closed channel also panics, so coordinate shutdown carefully in real producer/consumer code.