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

时间:2013-04-15 08:24:25

标签: go closures

这是我的代码(run):

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
}

输出:

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

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

2 个答案:

答案 0 :(得分:36)

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

4 4 4 4 4

输出的一部分。

'part 3'不会捕获其闭包中的任何外部变量。正如specs所说:

  

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

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

4 3 2 1 0

部分输出因为:

  

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


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

<强>但

当defer语句执行时,

'defer f(e)'中的表达式'e'被评估

答案 1 :(得分:0)

我想提出另一个示例,以增进对defer mechanish的理解,首先运行此代码段,然后切换标记为(A)和(B)的语句的顺序,然后查看结果给自己。

package main

import (
    "fmt"
)

type Component struct {
    val int
}

func (c Component) method() {
    fmt.Println(c.val)
}

func main() {
    c := Component{}
    defer c.method()  // statement (A)
    c.val = 2 // statement (B)
}

我一直想知道在这里适用哪些正确的关键字或概念。看起来表达式c.method已被求值,因此返回了绑定到组件“ c”的实际状态的函数(如获取组件内部状态的快照)。 我想答案不仅涉及defer mechanish,还涉及funtions with value or pointer receiver的工作方式。请注意,还会发生以下情况:如果将名为method的功能更改为pointer receiver,则延迟将c.val打印为2,而不是0。