SEANK.H.LIAO

go iterators as with

magic function scoping

go iterators as python's with

Go 1.23 introduced a new feature, range over function types or more commonly known as "iterators".

What's special about these is the way control flow works. The loop body is converted to a function, passed to the interator, but a return (or any other change in control flow) from the loop body skips through enclosing iterator, while still calling defers, affecting control flow of the outer function. Example:

 1package main
 2
 3import "fmt"
 4
 5func Iterator(yield func() bool) {
 6        fmt.Println("start")
 7        defer fmt.Println("done")
 8
 9        yield()
10}
11
12func foo() {
13        for range Iterator {
14                fmt.Println("inside loop")
15                return
16        }
17        fmt.Println("outside loop")
18}
19
20func ExampleIterator() {
21        // Output:
22        // start
23        // inside loop
24        // done
25        foo()
26}

What's this useful for? Well it's a lot like python's with blocks:

 1package main
 2
 3import (
 4        "io"
 5        "os"
 6)
 7
 8func OpenFile(name string) func(func(*os.File, error) bool) {
 9        return func(yield func(*os.File, error) bool) {
10                f, err := os.Open(name)
11                if err == nil {
12                        defer f.Close()
13                }
14                yield(f, err)
15        }
16}
17
18func main() {
19        for f, err := range OpenFile("hello.txt") {
20                if err != nil {
21                        // handle error
22                }
23                _, _ = io.ReadAll(f)
24        }
25}

Or something like:

 1package main
 2
 3import "sync"
 4
 5type mutex struct {
 6        m sync.Mutex
 7}
 8
 9func (m *mutex) do(yield func() bool) {
10        m.m.Lock()
11        defer m.m.Unlock()
12        yield()
13}
14
15func main() {
16        var mu mutex
17        for range mu.do {
18                // do protected thing
19        }
20
21        // old way
22        mu.do(func() bool {
23                // do protected thing
24        })
25}