一、for range的坑

相信小伙伴都遇到过以下的循环变量的问题,那是因为循环的val变量是重复使用的,即仅有一份。也就是说,每次循环后赋给val的值就会把前面循环赋给val的值替换掉,所以打印出来的值都是最后一次循环赋给val的值。

1、例子

func fr1() {arr := []int{1, 2, 3}for _, val := range arr {go func() {time.Sleep(time.Millisecond * 100)fmt.Println(val)}()}time.Sleep(time.Second)}//输出结果:3 3 3 func fr2() {arr := [2]int{1, 2}arrnew := []*int{}for _, val := range arr {arrnew = append(arrnew, &v)}fmt.Println(*arrnew[0], *arrnew[1])//输出结果: 2 2}

2、解决方案

使用局部变量/临时变量,即可解决

func fr1() {values := []int{1, 2, 3, 4, 5}for _, val := range values {//在这加入临时变量val := val go func() {time.Sleep(time.Millisecond * 100)fmt.Println(val)}()}time.Sleep(time.Second)}//输出结果:2 3 1 4 5 或 5 3 4 1 2 等无序结果//===========================或者=============================func fr1() {values := []int{1, 2, 3, 4, 5}for _, val := range values {//传入变量go func(val int) {time.Sleep(time.Millisecond * 100)fmt.Println(val)}(val)}time.Sleep(time.Second)}//输出结果:2 3 1 4 5 或 5 3 4 1 2 等无序结果

二、官方解决方案

1、在Go1.21版本中,提前体验

可以设置GOEXPERIMENT=loopvar,就会解决以上问题,运行代码时就不会出现以上的坑。

Windows中设置举例

go env -w GOEXPERIMENT=loopvar或$env:GOEXPERIMENT="loopvar"
> go envset GO111MODULE=onset GOARCH=amd64set GOBIN=set GOCACHE=C:\Users\Administrator\AppData\Local\go-buildset GOENV=C:\Users\Administrator\AppData\Roaming\go\envset GOEXE=.exeset GOEXPERIMENT=loopvarset GOFLAGS=set GOHOSTARCH=amd64set GOHOSTOS=windowsset GOINSECURE=set GOMODCACHE=C:\Users\Administrator\go\pkg\mod

2、预计在 Go1.22 起,在go.mod中支持

新的 for 循环语义,将会在 go.mod 文件中的 go 行(版本声明大于等于 Go1.22 下)默认应用也就不用设置GOEXPERIMENT=loopvar