compare Range Over Channel with an alternative
Fast recall
Quick revision
Core answer
Practical understanding of Range Over Channel.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The order is queue-then-tombstone, and range follows it exactly:
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch {
fmt.Println(v) // 1, 2, 3 — all three, then the loop ends
}Executed: three values print, then the range terminates. The close never truncates the queue — it marks the end AFTER everything already sent. This losslessness is the property that makes defer close(out) safe in every producer: the values in flight at close time reach the consumer; the close only adds the ending.
It composes into the pipeline shutdown cascade: stage A finishes and closes its output; stage B's range receives A's remaining buffered values, processes them all, exits its own loop, and its defer closes B's output — the termination flows downstream WITH the data draining ahead of it. Every stage sees: all the data, then the end, then it passes both along. No dropped tail, no race between 'last value' and 'close' — the channel's own ordering guarantees the sequence.
compare Range Over Channel with an alternative
Direct Answer
Yes — range drains every buffered value first (each yielded normally), then sees the close and terminates. No value sent before close is ever skipped.
Deep Dive & Architecture
The ordered demo IS the semantics:
ring: [1, 2, 3]; closed: true. range receives 1 (ok true), 2 (ok true), 3 (ok true), then (zero, false) → exits. The value-close order is: all sends that happened-before close, then close. The range observes that exact order.
The alternative designs and why Go rejected them:
- close-clears-the-buffer: loses in-flight data; every producer would need an external flush signal; pipelines would need acknowledgment protocols. Unacceptable.
- close-blocks-further-receives (close = channel dead, unreadable): receivers couldn't drain — every close would strand the tail; consumers would need to poll length (which is racy) before close. Worse.
- The tombstone: zero added operations at close (set flag, wake), natural FIFO preservation, receivers keep receiving until data runs out — the minimal correct design. Every other channel behavior (comma-ok, range termination) derives from it.
The invariant stated for review: after close(ch) returns, the channel is a finite, immutable-in-length queue: its remaining deliveries are exactly the values already buffered, in order, then the permanent end-signal. No send can add (panics), no receive can skip or reorder. That's the contract — and the reason a pipeline's defer close is both a termination signal AND a guarantee that everything sent was delivered: two properties from one flag.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Range Over Channel, and when would you use it?
- When would you use Range Over Channel in a real project?
- What is a common mistake when using Range Over Channel?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.