こんにちは。Tomoyuki(@tomoyuki65)です。
Go言語(Golang)のサンプルコードでエラーハンドリングがあった際に、よく「panic」関数が使われたりしますが、実務におけるAPI開発ではほぼ使わないです。
この記事では、そんなGo言語(Golang)におけるpanicについてまとめます。
Go言語(Golang)におけるpanicについて
Go言語(Golang)のpanic関数とは、プログラムの通常の処理を中断し、異常終了状態に入るための仕組みです。
回復不能なエラーが発生した際に使われるものですが、基本的な役割としては以下の通りです。
・現在の処理を即座に中断する
・スタックトレースを出力してプログラムを終了する(recover処理が無ければ)
実務におけるAPI開発のエラーハンドリング時はどうする?
上記の通り、panic関数は回復不能なエラーが発生した際に使われるものであり、panic関数を使うとサーバーが止まってしまうため、実務におけるAPI開発で使うことはほぼありません。
そのため、実務におけるAPI開発のエラーハンドリング時については、エラー系のレスポンス結果を返すようにするのが基本になります。
panic関数を使う場面はある?
package main
import (
"fmt"
)
func main() {
// 何らかのコンフィング設定の初期化処理を実行
config, err := InitConfig()
if err != nil {
// 後続処理で必須のため、panicで終了させる。
panic(fmt.Errorf("コンフィグ設定の初期化に失敗しました。: %w", err))
}
・・・ configの利用が必須な後続処理 ・・・
}
それ以外で使う場面があるとすれば、デバッグ時にpanicさせて処理を止めたい時に使うぐらいかなと思います。
逆にpanicを捉えてリカバリー処理をしたい場合はrecover関数を使う
逆にpanicが発生しても何らかのリカバリー処理を実行させたい場合は、「recover」関数を利用することで、同一関数内で発生したpanicを捉えてリカバリー処理をさせることが可能(ただし、ゴルーチンの関数は除く)です。
・panicを捉えてリカバリー処理をさせる例
package main
import (
"fmt"
)
// 関数f
func f() {
// 即時関数をdeferで実行
defer func() {
// 関数f内でpanicが発生するとrを取得してnil以外になりエラー処理を実行する
if r := recover(); r != nil {
// 何らかのリカバリー処理を実行
}
}()
// 何らかの処理を実行
}
func main() {
// 関数fを実行
f()
}
ただし、同一関数内で実行したゴルーチン内で発生したpanicは捉えられないので、その場合はゴルーチン内でリカバリー処理の記述が必要です。
package main
import (
"fmt"
)
// 関数f
func f() {
// ※ゴルーチン内で発生したpanicは捉えられないので注意!
defer func() {
if r := recover(); r != nil {
// 何らかのリカバリー処理を実行
}
}()
// ゴルーチンを実行する場合
go func() {
// ゴルーチンを使う場合は最初にリカバリー処理を記述する
defer func() {
if r := recover(); r != nil {
// 何らかのリカバリー処理を実行
}
}()
// ゴルーチン内の何らかの処理を実行
}()
}
func main() {
// 関数fを実行
f()
}
※ただし、ゴルーチン内でリカバリー処理を記述しても、panicが起きたゴルーチンはリカバリー処理後に終了するので注意
recover関数の乱用は危険なので注意
上記のようにrecover関数を利用すればpanicを捉えてリカバリー処理を実行できますが、以下のようにリカバリーすべきでないpanicもあるため、その点は注意しましょう。
・runtimeの致命的なエラー(Goの内部整合性が壊れた場合)
・処理を止めるべき設計としているpanic
最後に
Go言語(Golang)におけるpanicについてまとめました。
実務のAPI開発においてはpanic関数を使うことはほぼありませんが、逆にrecover関数を使ってリカバリー処理を記述する可能性はあるので、panicやrecoverに関する理解もしておくのは大事です。


コメント