• Go 1.22 中的 for 循环新特性详解


    目录

    每次迭代都创建新变量

    支持整数类型循环

    小结


    在 Go 语言中,for 循环是实现迭代的主要方式。Go 中的 for 循环非常灵活,有多种使用方式,包括传统的三部分 for 循环、类似于其他语言中的 while 循环以及迭代集合的 range 循环。

    在 1.22 之前的版本中,for 循环的变量只创建一次,在每个迭代中为这个变量赋予对应的值。由于这个特性,使用起来很容易犯错,一不小心就会导致意想不到的行为。看如下示例:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func main() {
    7. s := []string{"a", "b"}
    8. for _, v := range s {
    9. go func() {
    10. fmt.Print(v)
    11. }()
    12. }
    13. time.Sleep(time.Second * 1)
    14. }

    这个示例结果一般都会输出 bb,而并非预期中的 ab 或 ba。for 循环遍历 s 切片,每次迭代中都启动一个新的 goroutine 来打印变量 v 的值。由于 goroutine 是并发执行的,并且 for 循环不会等待它们完成。由于 for 循环的迭代速度非常快,当 goroutine 启动时,都共享了循环变量 v 的同一个实例,而不是在每次迭代时捕获 v 的值。当这些 goroutine 最终执行时,循环变量 v 的值大多数情况下已经变成了循环的最后一个值 b。但是由于并发的性质,实际的输出可能会有所不同。

    当然可以通过其他写法来避免这个问题,每次可以额外使用一个新的变量来承接,示例代码如下:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func main() {
    7. s := []string{"a", "b"}
    8. for _, v := range s {
    9. newV := v
    10. go func() {
    11. fmt.Print(newV)
    12. }()
    13. }
    14. time.Sleep(time.Second * 1)
    15. }

    也可以使用通过给闭包函数传参的方式,示例代码如下:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func main() {
    7. s := []string{"a", "b"}
    8. for _, v := range s {
    9. go func(v string) {
    10. fmt.Print(v)
    11. }(v)
    12. }
    13. time.Sleep(time.Second * 1)
    14. }

    这两种方式都略显繁琐。有了这个知识背景,接下来看下 Go 1.22 中 for 循环的第一个变化。

    每次迭代都创建新变量

    在 Go 1.22 中,for 循环的每次迭代都会创建新变量,这将会避免上文示例中的问题。还是按照第一个代码示例执行一次,代码如下:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func main() {
    7. s := []string{"a", "b"}
    8. for _, v := range s {
    9. go func() {
    10. fmt.Print(v)
    11. }()
    12. }
    13. time.Sleep(time.Second * 1)
    14. }

    可以发现这段代码不再输出 bb 而是输出了预期中的 ab 或者 ba。因为每次迭代都会创建新变量 v,所以每个闭包都持有对不同 v 变量的引用,因此,输出了预期的结果。循环的每次迭代都会创建新变量的这一更新,提高了代码的安全性和可预测性。接下来看下 Go 1.22 中 for 循环的第二个变化。

    支持整数类型循环

    在 Go 1.22 之前,for range 循环支持 array、slice、string、map 和 channel 类型的表达式。从 1.22 开始,新增了对整数类型表达式的支持。这意味着可以直接使用整数进行循环,例如如下代码示例,将迭代从 0 到 2 的整数。

    1. package main
    2. import "fmt"
    3. func main() {
    4. for i := range 3 {
    5. fmt.Println(i)
    6. }
    7. }

    这种写法在 1.22 之前的版本会报错。

    小结

    这两项更新显著提高了 Go 语言在编写循环时的灵活性和安全性,有助于避免一些常见的编程错误和陷阱。

  • 相关阅读:
    Spark repartitionAndSortWithinPartitions
    sonarqube二次安装启动报错解决方法
    Linux:进程管理和调度
    分支与循环(1)
    kafka Streams消费最新的消息
    深入理解Java正则表达式及其应用
    c++的引用和指针
    c3p0,DBCP,Druid(德鲁伊)数据库连接池
    酒店订房退房管理系统(数组应用)
    力扣第406题 根据身高重建队列 c++ 贪心思维
  • 原文地址:https://blog.csdn.net/luduoyuan/article/details/136264088