A range loop iterates over values produced by a rangeable expression. In Go 1.23 and later, the compiler also lets it consume custom iterators, so code can process values as they arrive without depending on whether the producer uses a slice, channel, or another structure. section 6.3.1
When consuming an iterator, a range loop:
- Receives each successive value while the iterator supplies it. section 6.3.3
- Stops naturally when the iterator is depleted. section 6.3.3
- Supports early termination through return or break; the compiler then causes yield to return false so a well-behaved iterator can stop producing values. section 6.3.3
- Requires a nil check when the iterator may be absent, because ranging over a nil iterator panics. section 6.3.3
Summarizing streamed results
func Summarize(results Results) Summary { var s Summary if results == nil { return s } for r := range results { s.Requests++ s.Bytes += r.Bytes if r.Error != nil || r.Status != http.StatusOK { s.Errors++ } } return s } Here, each Result is processed as the iterator produces it. The summarizer depends on the iteration contract rather than the underlying source, and could stop early with return or break. section 6.3.3
Listing 6.3 Implementing the Results iterator (hit/result.go)
package hit
import (
"iter" #1
"time"
)
// Results is an iterator for [Result] values.
type Results iter.Seq[Result] #2
. . .
Inspect the Results iterator declaration, which adapts iter.Seq[Result] for range-loop consumption. section 6.3.1


