Go

Go 1.23 range-over-func: what does this buggy iterator do?

Question 510HardGo 1.22 to 1.25
func Count(n int) iter.Seq[int] {
    return func(yield func(int) bool) {
        for i := range n {
            yield(i) // BUG: ignores the return value
        }
    }
}

func main() {
    for x := range Count(5) {
        if x == 2 {
            break
        }
        fmt.Println(x)
    }
}

It prints 0 and 1, then panics: runtime error: range function continued iteration after function for loop body returned false.

With range-over-func, the compiler turns the loop body into the yield callback. break, return, and a goto out of the loop all make yield return false. The iterator must stop calling yield after that. The runtime checks this and panics instead of silently running the body after a break. Correct version:

func Count(n int) iter.Seq[int] {
    return func(yield func(int) bool) {
        for i := range n {
            if !yield(i) {
                return
            }
        }
    }
}

Related points: defer inside the loop body belongs to the enclosing function, not to the callback. Iterators that hold resources should release them in the iterator with defer, which runs when the iterator returns after yield reports false. Use iter.Pull when you need to drive an iterator yourself, and always call the returned stop.

More on Tricky Output & Code-Review Puzzles

All 38 Tricky Output & Code-Review Puzzles questions