how to understand scope in go closure

huangapple go评论77阅读模式
英文:

how to understand scope in go closure

问题

有人知道为什么 case1 输出相同的结果,而 case2 输出顺序结果吗?
我知道 case1 输出相同值的原因是 functions 切片中每个函数的闭包都访问相同的作用域。

但是为什么在每个循环中添加 i:=i 后,case2 可以输出顺序结果呢?
在每个循环中重新定义 i 后,是否会生成一个新的作用域?
类似于 JavaScript 中的 let?

case1

func main() {
	funcs := []func() {}

	for i:=0;i<10;i++{
		funcs = append(funcs, func() {
			fmt.Println(i)
		})
	}

	for i:=0;i<10;i++{
		funcs[i]()
	}
}

输出

10
10
10
10
10
10
10
10
10
10

case2

func main() {
	funcs := []func() {}

	for i:=0;i<10;i++{
		i := i
		funcs = append(funcs, func() {
			fmt.Println(i)
		})
	}

	for i:=0;i<10;i++{
		funcs[i]()
	}
}

输出

0
1
2
3
4
5
6
7
8
9
英文:

Does anybody know why the case1 output the same result, but the case2 output the sequential result?
I know the reason why case1 output the same value is that the closure of each function in functions slice access to the same scope.

But why after adding i:=i in each loop can case2 output the sequential result?
Does after redefining i in eachloop, a new scope is generated?
like let in javascript?

case1

func main() {
	funcs := []func() {}

	for i:=0;i&lt;10;i++{
		funcs = append(funcs, func() {
			fmt.Println(i)
		})
	}

	for i:=0;i&lt;10;i++{
		funcs[i]()
	}
}

output

10
10
10
10
10
10
10
10
10
10

case2

func main() {
	funcs := []func() {}

	for i:=0;i&lt;10;i++{
		i := i
		funcs = append(funcs, func() {
			fmt.Println(i)
		})
	}

	for i:=0;i&lt;10;i++{
		funcs[i]()
	}
}

output

0
1
2
3
4
5
6
7
8
9

答案1

得分: 1

这不是一个作用域问题,而是编译器决定变量 i 是按引用还是按值捕获的问题。这并不总是显而易见的。在 Go 中的 for 循环中,会重用迭代变量,也就是 i。在 case1 中,变量 i 被捕获为引用,因为编译器认为在捕获之后它发生了变化。在 case2 中,内部变量 i 在捕获之前被创建、捕获和释放,因此编译器认为它是不变的,并按值进行捕获。结果是,在 case1 中运行函数时,结果都是最终值,因为变量 i 最终的值就是这样,每个函数只持有对该特定 i 的引用。在 case2 中,每个捕获在创建时都传递了一个 i 的“副本”,因此它们显示了你期望的值。

英文:

This isn't really a scope issue but rather an issue with how the compiler decides if your variable i is captured by reference or by value. This is not always obvious. For loops in go reuse the iteration variable in this case the i. In case1 the variable i is captured as a reference as the compiler sees it as being changed after the capture has taken place. In case2 the inner variable i is created before the capture, captured and released thus the compiler sees it as unchanging and captures it by value. The result is when the functions are run in case1 the results are all the final value as that is what the variable i ended up as and each function only holds a reference to that particular i. In case2 each capture was passed a "copy" of i at the time of creation thus they show the values you expect.

huangapple
  • 本文由 发表于 2021年8月3日 12:39:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/68630449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定