defer
はそれが定義された関数が終わったタイミングで実行されるので、forループでdeferを定義してしまうと、forループが終わって所属する関数が終わったタイミングで一斉に実行される。
package main import "fmt" func main() { for i := 1; i <= 5; i++ { println(fmt.Sprintf("Main %d", i)) defer closeResource(i) } } func closeResource(i int) { println(fmt.Sprintf("Close %d", i)) }
例えばこれを実行すると
Main 1 Main 2 Main 3 Main 4 Main 5 Close 5 Close 4 Close 3 Close 2 Close 1
こういう出力になる。これはDBコネクションなどの場合、リソースリークにつながるのであまりよろしくない。こういう場合はforのブロックを別関数に移すか、無名関数でラップするのが好ましい。
package main import "fmt" func main() { for i := 1; i <= 5; i++ { func() { println(fmt.Sprintf("Main %d", i)) defer closeResource(i) }() } } func closeResource(i int) { println(fmt.Sprintf("Close %d", i)) }
こうすれば、期待通りの結果になる
Main 1 Close 1 Main 2 Close 2 Main 3 Close 3 Main 4 Close 4 Main 5 Close 5
参考 go - `defer` in the loop - what will be better? - Stack Overflow