Goのdeferキーワードは、周囲の関数から退出するまでに指定された関数の実行を遅らせます — 関数はスタックに積まれ、逆順(LIFO)で実行されます。これはリソースの解放(ファイル、ミューテックス、接続など)によく使われます。
特性として、deferで渡された関数のすべての引数は、実行時ではなく、宣言時に即座に計算されることに注意が必要です。
func test() { for i := 0; i < 3; i++ { defer fmt.Println(i) // 印刷結果: 2, 1, 0 } }
次のコードは何を出力しますか?
func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) } }
答え:
2
1
0
理由は、各ループごとに引数iが即座に計算され、すべての値がdeferのスタックに保存されるからです。
ストーリー
ファイルサービスでは、deferが関数から正常に退出する際のみ呼び出されることを考慮するのを忘れていました。プログラムがdeferの呼び出し点以前に異常終了した場合、リークが発生しました。
ストーリー
データパイプラインでは、接続の閉鎖にdeferを使用していましたが、実際には全関数の終了後にのみ閉じられ、各イテレーションごとに閉じられなかったため、リソースが枯渇しました。
ストーリー
ロガーでは、引数が呼び出し時に計算されることを期待して無名関数でdeferを使用しました。そのため、ログの最後には不正確な情報が含まれ、値が以前にキャプチャされていました。