As mentioned yesterday, today we’re taking a bit of a step back to an earlier portion of the spec, which was just updated.
Here’s the first part of the updated section of the spec (new additions emphasized):
For statements with
range
clauseA “for” statement with a “range” clause iterates through all entries of an array, slice, string or map, values received on a channel, integer values from zero to an upper limit [Go 1.22], or values passed to an iterator function’s yield function [Go 1.23]. For each entry it assigns iteration values to corresponding iteration variables if present and then executes the block.
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
The expression on the right in the “range” clause is called the range expression, its core type must be an array, pointer to an array, slice, string, map, channel permitting receive operations, an integer, or a function with specific signature (see below).
Range expression 1st value 2nd value function, 0 values f func(func() bool) function, 1 value f func(func(V) bool) value v V function, 2 values f func(func(K, V) bool) key k K v V
I think this is a good place to start. With these three function signatures. The second and third of these signatures are explicitly named in the iter
package, but the named types (Seq
and Seq2
) are not part of the spec, and can largely be ignored for many purposes.
So what do these three function signatures do?
They define an iterator. An iterator is simply a special function—one with the requisite signature—that the Go compiler takes special notice of, when used as the expression following the range
keyword in a for loop:
for x, y := range iterator {
/* do stuff */
}
Don’t worry, we’ll get into those nitty gritty details, and what tha code actually does, and why, later.
For now, let’s focus on the difference between the three function signatures.
In it’s fullest form, a for
loop iterates over two values, a key and a value, or an index and a value.
This is what the third of these function signatures (aka iter.Seq2
) is for. It allows creating an iterator function that iterates over pairs of values.
Naturally, the second in the list, (aka iter.Seq
) is for iterating over a sequence of single values, where no key, or index, is appropriate.
And as we continue through the list in reverse order, we come to the first in the list, which isn’t explicitly named in iter
, and is unlikely to be seen often in real code, which iterates over no values. That is to say, it iterates (it’s executed more than once), but returns no values. This might be useful if you need to do a thing until a condition is reached, but no return value is required. Functional programmers are silently vomiting in their mouths as they read this.
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)