Goには try-catch のような例外構文はありません。
その代わりに、次の3つを組み合わせて 異常系と後始末 を扱います。
-
defer:関数終了時に必ず実行する処理 -
panic:致命的なエラーで処理を中断する -
recover:panicから復帰する(※ defer 内でのみ有効)
1. defer の基本動作
defer は その関数が終了する直前 に実行されます。
funcTestDefer() {
defer fmt.Println("END")
fmt.Println("START")
}
呼び出し:
TestDefer()
出力:
START
END
ポイント
-
returnがあってもdeferは実行される -
panicが起きてもdeferは実行される
2. defer は後始末に使う(ファイル操作)
Goでは、リソースを確保したら すぐに defer で解放処理を書くのが定石です。
file, err := os.Create("test.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
file.Write([]byte("Hello\n"))
なぜ defer なのか?
- 途中で
returnしても必ず close される - エラーが増えても後始末を書き忘れにくい
👉 「open したらすぐ defer close」 はGoの鉄則
3. defer の実行順序(LIFO)
defer は 後に登録したものから先に実行されます。
fmt.Println("START")
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
fmt.Println("END")
出力:
START
END
defer 2
defer 1
👉 defer は スタック(LIFO)構造。
4. panic とは?
panic は プログラムを異常終了させる仕組みです。
panic("runtime error")
-
panicが呼ばれると通常の処理は中断される - その関数に登録された
deferは 必ず実行される - 何も
recoverしなければプログラムは終了する
5. recover とは?
recover() は、発生した panic を捕まえて 処理を継続するための関数です。
超重要なルール
recover は defer の中で呼ばれたときだけ有効
6. panic / recover の正しい基本形
以下が panic を安全に recover する最小構成です。
package main
import"fmt"
funcmain() {
deferfunc() {
if x :=recover(); x !=nil {
fmt.Println("recovered:", x)
}
}()
fmt.Println("START")
panic("runtime error")
fmt.Println("NEVER PRINT")
}
出力:
START
recovered: runtime error
処理の流れ
-
deferが登録される -
panicが発生 -
deferが実行される -
recover()がpanicを捕まえる -
mainが正常終了する
7. panic が起きても defer は必ず実行される
panic が起きても、登録済みの defer はすべて実行されます。
defer fmt.Println("A")
defer fmt.Println("B")
panic("error")
出力:
B
A
panic: error
👉 そのため、recover を使わない場合でも
後始末(close / unlock など)は保証される。
8. panic / recover の使いどころ
基本方針
- 通常のエラー →
errorを返す - 想定外・不変条件違反 →
panic - recover は アプリケーションの最上位層 で使うことが多い
例:
- HTTPサーバのミドルウェア
- goroutine の境界
- main関数
9. まとめ
-
deferは関数終了時に必ず実行される -
deferは後入れ先出し(LIFO) - ファイル操作など後始末に最適
-
panicは致命的な異常を表す -
recoverは defer 内でのみ有効 - panic が起きても defer は必ず実行される
参考