diff --git a/src/iter/iter.go b/src/iter/iter.go index 30f65f7e489..4ea919b0724 100644 --- a/src/iter/iter.go +++ b/src/iter/iter.go @@ -2,8 +2,186 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package iter provides basic definitions and operations -// related to iteration in Go. +/* +Package iter provides basic definitions and operations related to +iterators over sequences. + +# Iterators + +An iterator is a function that passes successive elements of a +sequence to a callback function, conventionally named yield. +The function stops either when the sequence is finished or +when yield returns false, indicating to stop the iteration early. +This package defines [Seq] and [Seq2] +(pronounced like seek—the first syllable of sequence) +as shorthands for iterators that pass 1 or 2 values per sequence element +to yield: + + type ( + Seq[V any] func(yield func(V) bool) + Seq2[K, V any] func(yield func(K, V) bool) + ) + +Seq2 represents a sequence of paired values, conventionally key-value +or index-value pairs. + +Yield returns true if the iterator should continue with the next +element in the sequence, false if it should stop. + +Iterator functions are most often called by a range loop, as in: + + func PrintAll[V any](seq iter.Seq[V]) { + for _, v := range seq { + fmt.Println(v) + } + } + +# Naming Conventions + +Iterator functions and methods are named for the sequence being walked: + + // All returns an iterator over all elements in s. + func (s *Set[V]) All() iter.Seq[V] + +The iterator method on a collection type is conventionally named All, +because it iterates a sequence of all the values in the collection. + +For a type containing multiple possible sequences, the iterator's name +can indicate which sequence is being provided: + + // Cities returns an iterator over the major cities in the country. + func (c *Country) Cities() iter.Seq[*City] + + // Languages returns an iterator over the official spoken languages of the country. + func (c *Country) Languages() iter.Seq[string] + +If an iterator requires additional configuration, the constructor function +can take additional configuration arguments: + + // Scan returns an iterator over key-value pairs with min ≤ key ≤ max. + func (m *Map[K, V]) Scan(min, max K) iter.Seq2[K, V] + + // Split returns an iterator over the (possibly-empty) substrings of s + // separated by sep. + func Split(s, sep string) iter.Seq[string] + +When there are multiple possible iteration orders, the method name may +indicate that order: + + // All returns an iterator over the list from head to tail. + func (l *List[V]) All() iter.Seq[V] + + // Backward returns an iterator over the list from tail to head. + func (l *List[V]) Backward() iter.Seq[V] + + // Preorder returns an iterator over all nodes of the syntax tree + // beneath (and including) the specified root, in depth-first preorder, + // visiting a parent node before its children. + func Preorder(root Node) iter.Seq[Node] + +# Single-Use Iterators + +Most iterators provide the ability to walk an entire sequence: +when called, the iterator does any setup necessary to start the +sequence, then calls yield on successive elements of the sequence, +and then cleans up before returning. Calling the iterator again +walks the sequence again. + +Some iterators break that convention, providing the ability to walk a +sequence only once. These “single-use iterators” typically report values +from a data stream that cannot be rewound to start over. +Calling the iterator again after stopping early may continue the +stream, but calling it again after the sequence is finished will yield +no values at all, immediately returning true. Doc comments for +functions or methods that return single-use iterators should document +this fact: + + // Lines returns an iterator over lines read from r. + // It returns a single-use iterator. + func (r *Reader) Lines() iter.Seq[string] + +# Pulling Values + +Functions and methods that accept or return iterators +should use the standard [Seq] or [Seq2] types, to ensure +compatibility with range loops and other iterator adapters. +The standard iterators can be thought of as “push iterators”, which +push values to the yield function. + +Sometimes a range loop is not the most natural way to consume values +of the sequence. In this case, [Pull] converts a standard push iterator +to a “pull iterator”, which can be called to pull one value at a time +from the sequence. [Pull] starts an iterator and returns a pair +of functions—next and stop—which return the next value from the iterator +and stop it, respectively. + +For example: + + // Pairs returns an iterator over successive pairs of values from seq. + func Pairs[V any](seq iter.Seq[V]) iter.Seq2[V, V] { + return func(yield func(V, V) bool) bool { + next, stop := iter.Pull(seq) + defer stop() + v1, ok1 := next() + v2, ok2 := next() + for ok1 || ok2 { + if !yield(v1, v2) { + return false + } + } + return true + } + } + +If clients do not consume the sequence to completion, they must call stop, +which allows the iterator function to finish and return. As shown in +the example, the conventional way to ensure this is to use defer. + +# Standard Library Usage + +A few packages in the standard library provide iterator-based APIs, +most notably the [maps] and [slices] packages. +For example, [maps.Keys] returns an iterator over the keys of a map, +while [slices.Sorted] collects the values of an iterator into a slice, +sorts them, and returns the slice, so to iterate over the sorted keys of a map: + + for _, key := range slices.Sorted(maps.Keys(m)) { + ... + } + +# Mutation + +Iterators provide only the values of the sequence, not any direct way +to modify it. If an iterator wishes to provide a mechanism for modifying +a sequence during iteration, the usual approach is to define a position type +with the extra operations and then provide an iterator over positions. + +For example, a tree implementation might provide: + + // Positions returns an iterator over positions in the sequence. + func (t *Tree[V]) Positions() iter.Seq[*Pos] + + // A Pos represents a position in the sequence. + // It is only valid during the yield call it is passed to. + type Pos[V any] struct { ... } + + // Pos returns the value at the cursor. + func (p *Pos[V]) Value() V + + // Delete deletes the value at this point in the iteration. + func (p *Pos[V]) Delete() + + // Set changes the value v at the cursor. + func (p *Pos[V]) Set(v V) + +And then a client could delete boring values from the tree using: + + for p := range t.Positions() { + if boring(p.Value()) { + p.Delete() + } + } +*/ package iter import ( @@ -15,11 +193,13 @@ import ( // Seq is an iterator over sequences of individual values. // When called as seq(yield), seq calls yield(v) for each value v in the sequence, // stopping early if yield returns false. +// See the [iter] package documentation for more details. type Seq[V any] func(yield func(V) bool) // Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs. // When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence, // stopping early if yield returns false. +// See the [iter] package documentation for more details. type Seq2[K, V any] func(yield func(K, V) bool) type coro struct{} @@ -45,10 +225,13 @@ func coroswitch(*coro) // no longer interested in next values and next has not yet // signaled that the sequence is over (with a false boolean return). // It is valid to call stop multiple times and when next has -// already returned false. +// already returned false. Typically, callers should “defer stop()”. // // It is an error to call next or stop from multiple goroutines // simultaneously. +// +// If the iterator panics during a call to next (or stop), +// then next (or stop) itself panics with the same value. func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) { var ( v V @@ -157,10 +340,13 @@ func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) { // no longer interested in next values and next has not yet // signaled that the sequence is over (with a false boolean return). // It is valid to call stop multiple times and when next has -// already returned false. +// already returned false. Typically, callers should “defer stop()”. // // It is an error to call next or stop from multiple goroutines // simultaneously. +// +// If the iterator panics during a call to next (or stop), +// then next (or stop) itself panics with the same value. func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) { var ( k K