かまたま日記3

プログラミングメイン、たまに日常

GolangでForループの中でdeferしない

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