Go 1.23 range-over-func: what does this buggy iterator do?
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
- Q508This compiles. Why does it panic at runtime?
- Q509Why does errors.Is return different results here?
- Q511What does this counter print, and how do you prove the bug?
- Q512Deleting from a slice in place: what do both lines print?
- Q513Embedding is not inheritance: what does this print, and which line does not compile?
- Q514Type switch puzzle: what does describe return for each input?