Go:将变量传递给匿名函数

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

Go: transfer var into anonymous function

问题

我在将一个变量传递给匿名函数时遇到了问题。有解决办法吗?

import "github.com/lxn/walk"

***

var openAction [12]*walk.Action
for i := 0; i < 12; i++ {

    openBmp, err := walk.NewBitmapFromFile(_films[i][0])
    if err != nil {
        log.Printf("打开位图以构建主体:%v\n", err)
    }
    openAction[i] = walk.NewAction()
    openAction[i].SetImage(openBmp)
    openAction[i].SetText(_films[i][2])
    openAction[i].Triggered().Attach( func(){
        exec(i)
    })
    mw.ToolBar().Actions().Add(openAction[i])
}

exec(i) 中的 i 始终等于 11

英文:

I am having trouble transferring a variable into an anonymous function. Is there a solution?

import  &quot;github.com/lxn/walk&quot;

***

var openAction [12]*walk.Action
for i := 0; i &lt; 12; i++ {

    openBmp, err := walk.NewBitmapFromFile(_films[i][0])
    if err != nil {
        log.Printf(&quot;Open bitmap for buildBody() :%v\n&quot;, err)
    }
    openAction[i] = walk.NewAction()
    openAction[i].SetImage(openBmp)
    openAction[i].SetText(_films[i][2])
    openAction[i].Triggered().Attach( func(){
        exec(i)
    })
    mw.ToolBar().Actions().Add(openAction[i])
}

exec(i) where i always = 11

答案1

得分: 11

for i := 0; i < 12; i++ {
i := i
...
疯狂的是,这是你在Go代码中会看到的东西。这是闭包的工作方式和变量作用域的结果。你的匿名函数是一个捕获了i的闭包。具体来说,它捕获的是一个叫做i的变量,而不是i的当前值,并且它捕获的是在作用域中的任何i。在你的原始代码中,这是循环变量,对于循环的每次迭代都是相同的变量。所有的闭包都捕获了同一个变量。添加i := i声明了一个新的变量,在每次迭代中都会创建一个新的变量。现在每个闭包都将捕获这个新的变量,并且在每次迭代中它都会是一个不同的变量。

稍微详细一点,循环变量i的作用域是for语句。这包括循环块,但由于循环变量i的声明在块外部,所以在块内部使用相同名称声明一个新变量是合法的,并在块中的那一点创建一个新变量。然后,循环变量被遮蔽。通常,像这样声明的变量会被放在堆栈上,但在这种情况下,编译器逃逸分析发现当闭包在块结束时仍然引用这个块变量时,变量被放在堆上。在每次迭代中,块被重新进入,并在堆上放置一个新的变量i。

英文:
for i := 0; i &lt; 12; i++ {
    i := i
    ...

Crazy as it looks, this is something you will see in Go code. It results from the way closures work and the way variables are scoped. Your anonymous function is a closure that captures i. Specifically, it is capturing a variable called i, not the current value of i, and it captures whatever i is in scope. In your original code this is the loop variable, which is the same variable for each iteration of the loop. All of your closures captured the same variable. The addition of i := i declares a new variable on each iteration. Now each closure will capture this new variable, and on each iteration it will be a different variable.

In a little more detail, the scope of the loop variable i is the for statement. This includes the loop block, but since the declaration of the loop variable i is outside of the block, declaring a new variable with the same name inside the block is legal and creates a new variable at that point in the block. The loop variable is then shadowed. Often a variable declared like this goes on the stack, but in this case compiler escape analysis sees that your closure is still referring to this block variable when it goes out of scope at the end of the block, and so the variable is placed on the heap. On each iteration, the block is reentered and a new variable i is placed on the heap.

答案2

得分: 6

我认为这样可以得到你想要的结果:

openAction[i].Triggered().Attach(func(x int) func() {
    return func() { exec(x) }
}(i))

关键是让你的匿名函数返回一个匿名函数,每个创建的函数都会封闭i的每个值。

英文:

I think that this will get you what you want:

openAction[i].Triggered().Attach(func(x int) func() {
    return func() { exec(x) }
}(i))

The trick is to have your anonymous function return an anonymous function, and each created function will enclose each of the values of i.

答案3

得分: 4

你遇到了Go语言for循环的一个特殊情况。循环中的变量i并不是每次迭代都是一个新的变量。因此,所有的闭包都会关闭在同一个变量上,而这个变量的值在它们下面发生了变化。当你的代码在循环之后运行时,所有的函数都会看到它们所关闭的变量的值为11。

解决方法是将i传递给一个函数,然后该函数返回另一个闭包函数,闭包函数会关闭在函数参数上。这就是为什么Adam Crosslands的解决方案有效。

英文:

You are encountering a quirk of go's for loops. The i variable in the loop is not a new variable for each iteration. because of this all of your closures are closing over the same variable whose value is changing underneath them. When your code runs after the loop all of the functions see the value 11 for the i they closed over.

The solution is to pass the i into a function which then returns another function that closes over the functions arg. This is why Adam Crosslands solution works.

huangapple
  • 本文由 发表于 2012年4月12日 10:10:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/10116507.html
匿名

发表评论

匿名网友

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

确定