Why Go 1.23’s New Iterator Breaks Nested Loops and How to Spot It
The article reveals a bug in Go 1.23’s newly added iterator that corrupts results in three‑level nested loops, demonstrates the issue with a reproducible code sample, explains the underlying cause, offers a temporary compile‑flag workaround, and notes the imminent fix.
Reproducing the nested iterator bug in Go 1.23.2
The following program uses the new slices.Values iterator three times, each inside a nested for loop. It concatenates every element from the innermost loop into a string and returns the final result.
package main
import (
"fmt"
"slices"
)
func Bug(s1, s2, s3 []string) string {
var c1 string
for v1 := range slices.Values(s1) {
var c2 string
for v2 := range slices.Values(s2) {
var c3 string
for v3 := range slices.Values(s3) {
c3 = c3 + v3
}
c2 = c2 + v2 + c3
}
c1 = c1 + v1 + c2
}
return c1
}
func main() {
got := Bug([]string{"1", "2", "3"}, []string{"a", "b", "c"}, []string{"A", "B", "C"})
want := "1aABCbABCcABC2aABCbABCcABC3aABCbABCcABC"
if got != want {
fmt.Println(fmt.Errorf("got %v, want %v", got, want))
}
}With the expected concatenation 1aABCbABCcABC2aABCbABCcABC3aABCbABCcABC, the actual output produced by Go 1.23.2 differs (see screenshot). The discrepancy appears only when iterators are nested; a single‑level iterator works correctly.
Root cause and temporary mitigation
Investigation of the Go compiler source shows that the optimizer inlines the iterator helper functions. Inlining changes the lifetimes of the temporary variables that hold the iterator state, causing the innermost loop to reuse the same buffer across iterations. This leads to missing characters in the final string.
Compiling with inlining disabled prevents the incorrect reuse: go build -gcflags='-l' . The flag -gcflags='-l' disables function inlining, restoring the correct output. However, disabling inlining is unsuitable for production because it degrades performance.
Fix and broader implications
The bug was filed immediately after discovery, and a patch that adjusts the iterator implementation to preserve separate state per call was submitted the next day. The patch is slated for inclusion in the first minor release after 1.23.2.
Prior to this regression, many experienced Go developers had already expressed concerns about the complexity and maintainability of the iterator feature introduced in Go 1.23. The regression reinforces the risk of adding highly clever, low‑level optimizations without exhaustive testing, especially when those optimizations interact with nested constructs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
BirdNest Tech Talk
Author of the rpcx microservice framework, original book author, and chair of Baidu's Go CMC committee.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
