Go言語のdeferによる遅延実行の仕組みをわかりやすく解説

スポンサーリンク
スポンサーリンク

deferの概要

遅延実行 Goの予約語

defer

概要deferは、関数の実行を遅延させ、現在の関数が終了する直前に実行するためのキーワードです。

  • 主にリソースの解放(ファイルのクローズやロックの解除)などに使用される。
  • 複数のdeferを指定した場合は、後に指定したものから順に実行される(LIFO: 後入れ先出し)。
  • 関数の終了直前に実行されるため、例外が発生しても実行される(パニック回復にも利用可能)。

基本的なdeferの使い方

deferを使うと、関数が終了する直前に処理を実行できます。

package main

import "fmt"

func main() {
    fmt.Println("開始")
    
    defer fmt.Println("遅延実行")
    
    fmt.Println("終了")
}

解説:

  • defer fmt.Println("遅延実行")は、関数の最後に実行されるように遅延されます。
  • 通常の処理は開始 → 終了の順で実行されますが、deferにより最後に「遅延実行」が出力されます。

実行結果:

開始
終了
遅延実行

複数のdeferを使用した場合の実行順

deferを複数使うと、後に登録されたものから順に実行されます(LIFO: 後入れ先出し)。

package main

import "fmt"

func main() {
    defer fmt.Println("1番目のdefer")
    defer fmt.Println("2番目のdefer")
    defer fmt.Println("3番目のdefer")

    fmt.Println("メイン処理")
}

解説:

  • 最初に「メイン処理」が出力されます。
  • その後、最後に登録したdeferから順番に実行されます(3番目 → 2番目 → 1番目)。

実行結果:

メイン処理
3番目のdefer
2番目のdefer
1番目のdefer

deferを使ったリソースのクリーンアップ

deferはファイルやデータベース接続のクローズ処理によく使われます。

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("example.txt")
    if err != nil {
        fmt.Println("ファイル作成エラー:", err)
        return
    }
    defer file.Close() // 関数終了時にファイルをクローズ

    file.WriteString("Hello, World!")
    fmt.Println("ファイルに書き込みました")
}

解説:

  • defer file.Close()を使うことで、関数が終了する際にファイルが自動的にクローズされます。
  • エラーハンドリングとリソース管理を簡潔に記述できるため、deferはよく使用されます。

実行結果:

ファイルに書き込みました

panicとrecoverを使ったエラーハンドリング

deferを利用すると、panicが発生した場合でもリカバリーできます。

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("パニックを回復:", r)
        }
    }()

    fmt.Println("通常の処理")
    panic("エラー発生!")
    fmt.Println("この行は実行されません")
}

解説:

  • recover()defer内で使用することで、panicの回復が可能になります。
  • panic("エラー発生!")でパニックを発生させても、プログラムがクラッシュせずに処理を続行できます。

実行結果:

通常の処理
パニックを回復: エラー発生!

注意事項

  • deferの引数は即時評価される: deferに渡す関数の引数は、その時点で評価されるため、後で変更されても影響しない。
  • deferは大量に使用しない: LIFOで実行されるため、メモリ消費が増加する可能性がある。
  • panicが発生してもdeferは実行される: deferを適切に利用すれば、リソースのリークを防ぐことができる。

よくある質問

Q: deferは必ず実行されますか?
A: はい。関数が終了する直前に必ず実行されます。ただし、プログラムがos.Exit()で終了すると実行されません。
Q: deferの実行順序はどうなりますか?
A: 後に指定したdeferが先に実行される(LIFO: 後入れ先出し)というルールがあります。
Q: deferの中で関数の戻り値を変更できますか?
A: はい。名前付き戻り値を使うことで、deferの中で値を変更することが可能です。
Q: deferを使うとどんなメリットがありますか?
A: 関数終了時に確実に実行されるため、ファイルクローズやロック解除などのリソース管理が簡潔に記述できます。

まとめ

  • deferを使うと、関数終了直前に処理を実行できる。
  • 複数のdeferを登録すると、後に登録したものから実行される(LIFO順)。
  • ファイルのクローズやパニックの回復など、リソース管理に適している。
  • 使用しすぎるとメモリ消費が増えるため、適切に利用することが重要。