Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围

简介: Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围

不再共享循环变量


在老版本的 Go 中,for 循环迭代器的变量是一个单一变量,在每个循环迭代中仅是取值不同。这样做在性能上非常高效,但如果使用不当,会导致意想不到的行为,可能会造成共享循环变量的问题。


最经典的场景就是在 goroutine 循环时的问题。如下代码:


func main() {
 values := []int{1, 2, 3, 4, 5}
 for _, val := range values {
  go func() {
   fmt.Printf("%d ", val)
  }()
 }
 time.Sleep(time.Second * 3)
}


输出结果:


5 5 5 5 5


如果是 Go1.22 以前的版本,在不做任何变更的情况下。我们需要把代码改成如下:


func main() {
 values := []int{1, 2, 3, 4, 5}
 for _, val := range values {
  go func(val int) {
   fmt.Printf("%d ", val)
  }(val)
 }
 time.Sleep(time.Second * 3)
}


其他场景下,也会需要写 v := v 的代码来再次赋值。经典的很。


但在 Go1.22 起,不再需要这么干了。之前文章《Go 团队将修改 for 循环变量的语义,Go1.21 新版本即可体验!》提到的 GOEXPERIMENT=loopvar 特性已经默认加到该版本。语法将默认改变。


也就是在 Go 1.22 中,for 循环的每次迭代都会创建新变量,每次循环迭代各自的变量,以避免意外共享错误。上面一模一样的代码,输出结果不再是固定的 5。


而是非固定、非稳定有序的值,例如:2 4 3 5 1 等随机结果。


如下输出:


// 运行第一次
2 3 1 4 5
// 运行第二次
2 3 1 5 4
// 运行第三次
5 1 2 3 4
...


很多面试官喜欢拿这个来做面试题。敲黑板了,请大家改一下面试题和答案了。


支持循环整数类型


在 for 循环中,对整数类型的范围进行循环迭代。代码如下:


func main() {
 for i := range 7 {
  fmt.Println("煎鱼", i)
 }
 fmt.Println("煎鱼进脑子了!")
}


在老版本的 Go 中,会直接报如下错误:


$ go run demo.go
# command-line-arguments
./demo.go:8:17: cannot range over 7 (untyped int constant)


但在 Go1.22 起,支持了该功能特性。


输出结果:


煎鱼 0
煎鱼 1
煎鱼 2
煎鱼 3
煎鱼 4
煎鱼 5
煎鱼 6
煎鱼进脑子了!


算是补全一个 for 循环的小缺漏。


总结


在 Go1.22 起,对 for 循环的共享变量进行了一次整治,新版本将会采用新的语法进行。


如果对你的老代码升级有影响,可以主动配置 GOEXPERIMENT=loopvar 来保持逻辑一致。


本次的整型类型范围的 for 循环支持,也算是填补和拓展了程序里的用法。


最后的最后,Go 面试题中有用到上述特性的同学,请注意修改问题和答案。免得没跟上新版本的迭代。

相关文章
|
2月前
|
Go 开发者
Go语言内存共享与扩容机制 -《Go语言实战指南》
本文深入探讨了Go语言中切片的内存共享机制与自动扩容策略。切片作为动态数组的抽象,其底层结构包含指针、长度和容量。多个切片可能共享同一底层数组,修改一个切片可能影响其他切片。当切片容量不足时,`append`会触发扩容,新容量按指数增长以优化性能。为避免共享导致的副作用,可通过`copy`创建独立副本或在函数中使用只读方式处理。最后总结了最佳实践,帮助开发者高效使用切片,写出更优代码。
|
2月前
|
Go 容器
Go语言变量与常量 -《Go语言实战指南》
本章详细介绍了Go语言中变量与常量的基础知识。变量支持多种声明方式,包括标准声明、类型推导和短变量声明等,未初始化的变量会自动赋零值。常量在声明时必须赋值,且运行时不可更改,支持使用`iota`实现枚举。两者的主要区别在于变量可变而常量不可变,变量有零值而常量必须初始化。此外,还强调了`:=`的使用限制及代码整洁性要求,并通过实践示例巩固理解。掌握这些内容是学好Go语言的关键基础。
|
8月前
|
Go
go语言中在 for 循环中使用 break
go语言中在 for 循环中使用 break
138 4
|
3月前
|
算法 Go
【LeetCode 热题100】深入理解二叉树结构变化与路径特性(力扣104 / 226 / 114 / 543)(Go语言版)
本博客深入探讨二叉树的深度计算、结构变换与路径分析,涵盖四道经典题目:104(最大深度)、226(翻转二叉树)、114(展开为链表)和543(二叉树直径)。通过递归与遍历策略(前序、后序等),解析每题的核心思路与实现方法。结合代码示例(Go语言),帮助读者掌握二叉树相关算法的精髓。下一讲将聚焦二叉树构造问题,欢迎持续关注!
83 10
|
8月前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
101 2
|
10月前
|
Go
初识go变量,使用var和:=来声明变量,声明变量的三种方式
这篇文章介绍了Go语言中使用`var`和`:=`声明变量的三种不同方式,包括声明单个或多个变量、通过值确定数据类型以及在函数体内使用`:=`声明局部变量。
初识go变量,使用var和:=来声明变量,声明变量的三种方式
|
5月前
|
算法 测试技术 Go
Go 1.24.0 重磅发布:新特性、新工具,开发者必看!
`Go 1.24.0` 已正式发布,带来诸多改进和新特性。语言层面上,泛型类型别名现已被完全支持;性能方面,通过优化 `map` 实现和内存分配,减少了 2-3% 的 CPU 开销;工具链新增模块工具依赖跟踪及测试分析器;标准库增加了弱引用包、FIPS 140-3 合规机制等;WebAssembly 支持也得到了增强。快来下载体验吧!
253 7
|
7月前
|
Go
go语言使用传统的for循环
go语言使用传统的for循环
82 7
|
7月前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
137 7
|
8月前
|
Go
go语言中控制循环
go语言中控制循环
76 14