同样的for-range,不同的知识

题目:

下面这段代码输出什么?为什么?

1
2
3
4
5
6
7
8
9
10
11
12
func main() {

var m = [...]int{1, 2, 3}

for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}

time.Sleep(time.Second * 3)
}

答案解析:

参考答案及解析:

1
2
3
2 3
2 3
2 3

​ 这里的for range 跟之前讲过的一样,变量 i、v 在每次循环体中都会被重用,而不是重新声明。

各个 goroutine 中输出的 i、v 值都是 for range 循环结束后的 i、v 最终值,而不是各个 goroutine 启动时的 i, v值。可以理解为闭包引用,使用的是上下文环境的值。两种可行的 fix 方法:

a.使用函数传递

1
2
3
4
5
for i, v := range m {
go func(i,v int) {
fmt.Println(i, v)
}(i,v)
}

b.使用临时变量保留当前值

1
2
3
4
5
6
7
for i, v := range m {
i := i // 这里的 := 会重新声明变量,而不是重用
v := v
go func() {
fmt.Println(i, v)
}()
}

​ 如果你简单得认为是上述那样,就错了。其实这样还有一个细节,就是这里每次输出都是一样的,是因为 切片内元素太少,导致 最开始创建的 goroutine 还没有开始执行,for range 就执行结束了。如果切片内的元素多一点,输出的结果就不一样了。

​ 所以这个题的本质,还是与之前的一样:for range 语句中定义的变量会被复用,而不是重新定义。

reference: https://tonybai.com/2015/09/17/7-things-you-may-not-pay-attation-to-in-go/


同样的for-range,不同的知识
http://example.com/2023/08/06/Go每日一题/同样的for-range,不同的知识/
作者
Feng Tao
发布于
2023年8月6日
更新于
2023年8月6日
许可协议