
Go 1.22 for 循环变量修复从 index bug 到闭包陷阱的完全解读Go 1.22 之前多少人栽过for i : range s在闭包里所有 goroutine 拿到同一个 i这一篇把 1.22 的修复讲透并给出兼容性写法。一、历史 Bug循环变量共享varfns[]func()fori:0;i3;i{fnsappend(fns,func(){fmt.Println(i)})}for_,f:rangefns{f()}// 1.21 输出 3 3 3不是 0 1 2原因循环变量i是同一个对象每次迭代都复用闭包捕获的是变量地址。二、1.22 的官方修复Go 1.22 引入loopvar : loopvar的语义每次迭代的i都是一个新变量于是// 1.22 输出 0 1 2对for range同样修复。注意是否声明go1.22或更高版本的go.mod才生效老版本需手写兼容。三、解决方案不论 Go 版本3.1 显式拷贝fori:0;i3;i{i:i// shadowfnsappend(fns,func(){fmt.Println(i)})}3.2 通过参数传入fori:0;i3;i{fnsappend(fns,func(idxint)func(){returnfunc(){fmt.Println(idx)}}(i))}3.3 改用 range1.22- 唯一安全for_,i:range[]int{0,1,2}{...}range表达式里i也是共享变量但range通常迭代局部 slice影响小。四、goroutine 并发遍历varwg sync.WaitGroupfor_,v:rangeitems{wg.Add(1)gofunc(item Item){deferwg.Done()process(item)}(v)// 传值}wg.Wait()go vet默认会捕获这种遗漏。五、1.22 其他 loop 相关改进range over intfor i : range 10直接生成 0-9for-range over slice优化索引变量类型int → uintptr六、闭环总结// 1.22funcmain(){varwg sync.WaitGroup list:[]int{10,20,30}for_,v:rangelist{wg.Add(1)gofunc(vint){deferwg.Done()fmt.Println(v)}(v)}wg.Wait()}七、踩坑清单1.22 之前忘记 shadow引出幽灵 buggo vet 永远开起来CI 必跑业务代码尽量for ... v : range不要再写for i : 0; ...; i配合 Closures testing单元测试覆盖并发场景八、总结与展望Go 1.22 修复了一个老 Gopher 都懂初学者必踩的循环变量陷阱。我们应当做的事升级到 1.22开go 1.22标记团队代码规范增加vet强制配合 staticcheck 多一层保护未来计划对循环里的常量传播、迭代次数常数化可能进一步优化。九、参考文献Go 1.22 Release Notescmd/compile: loop variablesEffective Go: Function literals / closures