NU:LOGiC Logo

【Go言語】log.Fatalを気軽に使ってはいけない理由

【Go言語】log.Fatalを気軽に使ってはいけない理由

Go言語におけるlog.Fatal は、エラー発生時にプログラムを即座に終了させる強力なツールです。ですが、この即座の終了がdeferで設定したリソースの後処理をスキップする危険性があります。この記事では、log.Fatalの紹介だけでなく、安全なエラーハンドリングとリソース解放のバランスを取るための代替手法も紹介します。

3行で要約すると

  • log.Fatal はエラーをログに出力した後、プログラムを即座に終了させる関数です。
  • log.Fatal の使用は defer によるリソースの後処理をスキップするため、注意が必要です。
  • 安全なエラーハンドリングとリソースの解放を確保するためには、log.Fatal の代わりに他の手法を考慮するべきです。

log.Fatal とは何か?

なぜこの関数があるのか

log.Fatal は Go 言語の標準ライブラリの一部であり、エラーメッセージを出力した後でプログラムを終了させるという、二つの役割を果たします。このような厳格なエラーハンドリングが求められる場面では便利な関数です。

コード例

file, err := os.Open("example.txt")
if err != nil {
    log.Fatal("Failed to open file:", err)
}

このコードは、example.txt ファイルを開くことに失敗した場合、エラーメッセージをログに出力してプログラムを終了します。

log.Fatal の落とし穴:defer スキップ

なぜ問題なのか

log.Fatal がプログラムを終了させる際には、os.Exit(1) が内部で呼び出されます。このため、defer 文によって指定された後処理がスキップされる可能性があります。

コード例

func main() {
    defer fmt.Println("This will not be printed.")
    
    file, err := os.Open("example.txt")
    if err != nil {
        log.Fatal("Failed to open file:", err)
    }
    
    // ファイル操作
}

この例では、defer fmt.Println("This will not be printed.") が実行されることなくプログラムが終了します。

より良い代替手法

なぜ他の手法を考えるべきか

後処理が重要な場合、特にリソースの解放やデータの保存が必要な場合、log.Fatal の代わりにエラーを適切にハンドリングする方法を検討するべきです。

コード例

file, err := os.Open("example.txt")
if err != nil {
    fmt.Println("Error:", err)
    os.Exit(1)
}
defer file.Close()

// ファイル操作

この例では、os.Exit(1) を明示的に呼び出す前に、defer file.Close() によってファイルが確実に閉じられるようにしています。

まとめ

log.Fatal は便利な関数ですが、defer による後処理をスキップする副作用があります。リソースの解放やデータの保存など、プログラムの後処理が重要な場合には、その使用は避け、代わりに他のエラーハンドリング手法を検討するべきです。