Slices, maps and strings · 3 MIN READ
range works on functions too
Write something that can be ranged over without building a slice first.
Since Go 1.23, range accepts a function as well as a slice, map or channel. A function of the shape func(yield func(V) bool) is an iterator: it calls yield once per element, and stops early when yield returns false — which is how a break in the caller's loop reaches you.
There are two shapes, and the standard library names them in iter: iter.Seq[V] yields one value per step, and iter.Seq2[K, V] yields two, which is what map-like iteration needs. You rarely write the type out; you write the function, and range accepts it.
The point is not novelty. Before this, a function returning a sequence had to hand back a whole slice — allocating every element even when the caller wanted the first one — or take a callback and lose break, continue and return. An iterator is lazy and keeps the loop keywords working, which is why slices.Values and maps.Keys return one instead of a copy.
SEE THE IDEA
The iterator offers values through yield
- 01
Producer
gofunc(yield func(int) bool)The iterator supplies each value
- 02
Consumer
gofor v := range valuesThe loop body receives it
- 03
Stop signal
goif !yield(v) { return }A false result ends production
The range loop and yield call cooperate to stop at the same boundary.
In practice
Keep iterator state local unless reuse is intentionally stateful. A rangeable function may yield no values, one value, or a key and value, according to its signature. As soon as yield returns false, stop calling it and release any resources owned by the iterator.
Watch out
Calling yield again after it returned false violates the iterator protocol and can panic.
Work through the exercise
package main
// Take should return an iterator over at most n values from the
// slice — nothing copied up front, and a caller that breaks out
// early must stop the work rather than run to the end.
func main() {
for value := range Take([]string{"go", "forge", "run"}, 2) {
println(value)
}
}Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.
Progress stays in this browser. No account needed.