Go言語クロージャーについての豆知識
背景
for分の中に関数を書くときに、特にfor分のスコープと関数の組み合わせによって、クロージャーが生成され、関数の実行タイミングはfor分と一致ではない場合は、バグは発生しやすいです。
例えば、javascriptでは以下のようなクロージャー典型例があります。
Javascriptの豆知識(let,var,クロージャーに関する面接問題)
Javascriptではletという変数宣言方法を活かすことによって、うまくfor分共にあるクロージャー問題点を解消できますが、Go言語だと、どうやって解消できますでしょうか
問題
コード
func main() {
n := 5
funcs := []func(){}
for i:=0;i<n;i++ {
// fmt.Println(&i) // コメントアウトを外してみたら、iのメモリアドレスは同じものです。
funcs = append(funcs, func() {
fmt.Print(i)
})
}
for i:=0;i<n;i++ {
funcs[i]()
}
}
出力
5
5
5
5
5
原因
原因は二つがあります
- これはfor分からループするタイミング(funcsスライスに関数を作って入れるタイミング)とfuncsスライスにある関数が実行されるタイミングは違います。
- funcスライスにあるすべてな関数にある変数iは同じところに参照しています。(for分ではメモリアドレスが同じなiを使っているためです)
- そのため、関数が実行するタイミングでは、すべてな関数から参照しているiはすでに5になりましたので、すべてな関数は同じi(10)を出力しました。
javascriptだと、for分にiをletで定義することによって、問題を解消できますが、Go言語はどうなるですか><?
いろいろ調べました。
解決方法1
新しい変数iを作ることによって、新たなiをメモリ場で開拓し、funcsスライスに入れる関数はこちらのiを記憶します。
コード
func main() {
n := 5
funcs := []func(){}
for i:=0;i<n;i++ {
i := i
// fmt.Println(&i) //コメントアウトを外して見たら、iのメモリアドレスはそれぞれ異なります。
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for i:=0;i<n;i++ {
funcs[i]()
}
}
出力
0
1
2
3
4
解決方法2
実行された関数Aが関数Bをreturnします、関数Bをfuncsスライスに入れます。関数Aに渡した引数は値渡しなので、関数Aが実行されるときに、iが新しく作られています。
これによって、関数Bは関数Aとクロージャーが生成され、関数Aの中に新たなiがメモリ場で開拓され、関数Bがそのiを参照します。それぞれの関数Bはそれぞれのiを参照しています。
コード
func main() {
n := 5
funcs := []func(){}
for i:=0;i<n;i++ {
funcs = append(funcs, func(i int) func() {
// fmt.Println(&i) // コメントアウトを外してみたら、iのメモリアドレスはそれぞれ異なります。
return func() {
fmt.Println(i)
}
}(i))
} // ここ → 関数Aにforループのiを渡し、関数Bを返されます。
for i:=0;i<n;i++ {
funcs[i]()
}
}
出力
0
1
2
3
4
ちなみに、関数Aに引数をポインター渡しとして実行したら、どうなりますか?
for i:=0;i<n;i++ {
funcs = append(funcs, func(i *int) func() {
fmt.Println(i)
return func() {
fmt.Println(*i)
}
}(&i))
}
当然、すべてなiが同じメモリアドレスに参照し、全てのfuncの出力は同じ5になります。
0xc00001c080
0xc00001c080
0xc00001c080
0xc00001c080
0xc00001c080
5
5
5
5
5
参考資料
Author And Source
この問題について(Go言語クロージャーについての豆知識), 我々は、より多くの情報をここで見つけました https://qiita.com/xu1718191411/items/70550780d928dec96cab著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .