Go 范围循环作用域和间接函数

Go Ranged Loop Scope and Indirect Function

有人可以帮助我了解以下关于 golang 循环范围和临时函数的惊喜的“原因”吗?以下是一些更复杂代码的摘录:

package main

import ( "fmt" )

type Caller struct {
    call func()
}

func printer(val int) {
    fmt.Printf("the value is %v\n", val)
}

func main () {
    values := []int{1,2,3}

    var callers []Caller
    for _,val := range values {
        call := func() { printer(val) }
        callers = append(callers, Caller{call})
    }

    for _, caller := range callers {
        caller.call()
    }
}

产生了(对我来说)令人惊讶的结果:

the value is 3
the value is 3
the value is 3

如果我通过将范围值循环体更改为以下内容来更改该代码:

        theVal := val
        call := func() { printer(theVal) }
        callers = append(callers, Caller{call})

那么我们得到了原本希望的结果:

the value is 1
the value is 2
the value is 3

从根本上说,一个有效,另一个无效 - 我很清楚这一点,可以尝试记住成语。我希望得到更多的理解,也许是 golang 中的一点人生教训。作用域规则和延迟执行意味着循环变量保持最终有效值并提交到循环期间构建的每个临时函数中是什么意思?为什么不将值“val”放入动态构建的函数“call”中?我怀疑我对一些基本的东西感到困惑。即使看到了一个工作版本,我也不确定我将来是否能够避免这样的陷阱。如果您对迭代值的“为什么”有这样的建议,我很想听听(并提前感谢您)。

这也适用于您。这是 FAQ 中的 link。

package main

import (
    "fmt"
)

type Caller struct {
    call func()
}

func printer(val int) {
    fmt.Printf("the value is %v\n", val)
}

func main() {
    values := []int{1, 2, 3}

    var callers []Caller
    for _, val := range values {
        var call func()
        func(v int) {
            call = func() {
                printer(v)
            }
        }(val)
        callers = append(callers, Caller{call})
    }
    for _, caller := range callers {
        caller.call()
    }
}

另一种方法是在每个闭包启动时将 val 的当前值绑定到每个闭包,您可以将它存储在一个新变量中然后使用它(您已经完成的解决它的方法)。