Go Weekly #2: Go 1.23 Iterators
Why People are Angry over Go 1.23 Iterators
Context:
- Most languages provide standardized way to iterate over values stored in containers.
Problem:
Go has
for
range
for maps, slices, strings, arrays and channels but no generic mechanism for user-written containers.Short list of some non-generic iterators:
runtime.CallersFrames: returns
runtime.Frames
iterates over stack frames;Frames
has aNext
method and a bool to check if there are more frames.bufio.Scanner: iterates through an
io.Reader
;Scan
method advances to the next value,Bytes
method to return the value andErr
to return error.database/sql.Rows: iterates through the results of a query; also has
Scan
method.
Before Generics were introduced, no way to write an interface that described an iterator that would cover all of the use cases…
Solution:
- Go 1.22 proposal of adding package
iter
can range over integers and functions:
type ( Seq[V any] func(yield func(V) bool) bool Seq2[K, V any] func(yield func(K, V) bool) bool )
(Seq2 represents a sequence of paired values: key-value, index-value or value-error)
- An iterator is a function that passes successive elements of a sequence to a callback function
yield
, it returns true if it should continue, false if it should stop.
type ( Yield[V any] func(V bool) Yield2[K, V any] func(K,V) bool )
- Examples:
func Backward[E any](s []E) func(func(int, E) bool) { return func(yield func(int, E) bool) { for i := len(s)-1; i >= 0; i-- { if !yield(i, s[i]) { // Where clean-up code goes return } } } } s := []string{"a", "b", "c"} for _, el in range Backward(s) { fmt.Print(el, " ") } // c b a
- Go 1.22 proposal of adding package
Conclusion:
- The
for range
is getting complex:- return true => continue
- return false => break
- Feels too functional than an imperative language
- Still the purpose is good, let’s wait to see the adoption in community after awhile
- The