Tomoki Ota's Blog

article icon

【Go言語】パニック

作成日 

はじめに

Goにはtry/catchがないが、panicやrecoverをむやみにつかってはいけない。一般的には、error型で返すのが好ましい。

panicとは

Go Playground

func main() {
    fmt.Println("a")
    panic("foo")
    fmt.Println("b")
}

パニックが発生すると、現在の goroutine がリターンするか、recover でパニックが捕捉されるまで、パニックはコールスタックをさかのぼる。

以下はrecoverで捕捉する例である。

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recover", r)
        }
    }()
 
    f() // パニックが発生するfをコールする
}
 
func f() {
    fmt.Println("a")
    panic("foo")
    fmt.Println("b")
}

recoverで捕捉されると、goroutineは終了しない。 recoverを呼び出すのは、deferに与えられた関数内だけで有効。recover関数はnilを返し、何も効果はない

パニックを発生させるべきとき

  • 純粋なプログラマーエラー
  • アプリケーションが必須依存関係を必要とするにも関わらず、初期化に失敗するとき

純粋なプログラマーエラー

net/httpパッケージのWriteHeaderでは、ステータスコードが有効化どうかを検査するためにcheckWriteHeaderCode()が呼ばれる。

func checkWriteHeaderCode(code int) {
    if code < 100 || code > 999 {
        panic(fmt.Sprintf("invalid WriteHeader code %v", code))
    }
}

ステータスコードが不正な値をとるとパニックを起こす。

その他にもdatabase/sqlパッケージのRegister()が0位に挙げられる。

アプリケーションが必須の依存関係の作成に失敗するとき

新たな顧客アカウントを作成するためのサービスを公開するとき、提供された電子メールアドレスの検証を正規表現を用いて行う。

regexpパッケージには、文字列から正規表現を作成する関数として、以下の2つがある。

  • Compile : *regexp.Regexpとエラーを返す
  • MustCompile : エラーが発生したらパニックを発生させる

まとめ

ほとんどの場合は、最後の戻り値として、error型を返すべきである。

参考文献

  1. 100 Go Mistakes and How to Avoid Them
この記事をシェアするx icon
アイコン画像
Tomoki Ota

フルスタックエンジニア。Goが好き。趣味はカメラと旅行です📷