golang如何"推迟"捕获闭包的参数?

Rec*_*Hou 32 closures go

这是我的代码(运行):

package main

import "fmt"

func main() {
    var whatever [5]struct{}

    for i := range whatever {
        fmt.Println(i)
    } // part 1

    for i := range whatever {
        defer func() { fmt.Println(i) }()
    } // part 2

    for i := range whatever {
        defer func(n int) { fmt.Println(n) }(i)
    } // part 3
}
Run Code Online (Sandbox Code Playgroud)

输出:

0 1 2 3 4 4 3 2 1 0 4 4 4 4 4

问题:第2部分和第3部分有什么区别?为什么第2部分输出"44444"而不是"43210"?

zzz*_*zzz 38

'part 2'闭包捕获变量'i'.当闭包(后面)中的代码执行时,变量'i'具有它在range语句的最后一次迭代中具有的值,即.'4'.因此

4 4 4 4 4
Run Code Online (Sandbox Code Playgroud)

部分输出.

"第3部分"不会捕获其闭包中的任何外部变量.正如规格所说:

每次执行"defer"语句时,将像往常一样评估调用的函数值和参数,并重新保存但不调用实际函数.

因此,每个延迟函数调用都具有不同的'n'参数值.它是执行延迟语句时'i'变量的值.因此

4 3 2 1 0
Run Code Online (Sandbox Code Playgroud)

部分输出因为:

...在周围函数返回之前,延迟调用以LIFO顺序执行...


要注意的关键点是当defer语句执行时,'defer f()'中的'f()'不会执行

当defer语句执行时,将评估 'defer f(e)'中的表达式'e' .