Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

  1. トップページ
  2. Go辞典
  3. panic() / recover()

panic() / recover()

『panic()』はGoで実行時エラーを発生させる関数で、通常のエラー処理(error型の返却)では対応できない深刻な問題を表します。『recover()』は『defer』内でpanicを捕捉して正常な処理に復帰するための関数です。

構文
// panicを発生させます。
panic(値) // 文字列・エラー・任意の値を渡せます。

// recoverでpanicを捕捉します(deferの中でのみ有効です)。
defer func() {
    if r := recover(); r != nil {
        // panicから回復します。rはpanicに渡された値です。
        fmt.Println("panicを捕捉:", r)
    }
}()

// panicが発生すると以下の順で処理されます。
// 1. 現在の関数の残りの処理をスキップ
// 2. 登録済みのdeferを実行(recoverがあれば捕捉)
// 3. 捕捉されなければ呼び出し元に伝播
// 4. 最終的にプログラムがクラッシュしてスタックトレースを出力
関数一覧
関数概要
panic(v)実行時エラーを発生させます。引数に任意の値を渡せます。
recover()defer内でpanicの値を捕捉して正常復帰します。panic中でなければnilを返します。
サンプルコード
package main

import "fmt"

// recoverでpanicを捕捉して安全に処理を終える関数です。
func safeDiv(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            // panicを捕捉してerrorとして返します。
            err = fmt.Errorf("パニックを回復しました: %v", r)
        }
    }()
    // b==0のときGoのランタイムがpanicを発生させます。
    result = a / b
    return
}

// panicが呼び出しスタックを伝播する例です。
func level3() {
    panic("level3でpanicが発生しました")
}

func level2() {
    level3()
}

func level1() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("level1でpanicを捕捉:", r)
        }
    }()
    level2() // level3のpanicがここまで伝播してdeferで捕捉されます。
}

// 不正な引数に対してpanicを使う例(通常はerrorを返すほうが推奨)
func mustPositive(n int) int {
    if n <= 0 {
        panic(fmt.Sprintf("正の整数が必要ですが %d が渡されました", n))
    }
    return n
}

func main() {
    // safeDiv:ゼロ除算のpanicをrecoverで捕捉します。
    result, err := safeDiv(10, 2)
    fmt.Printf("10 / 2 = %d, err = %v\n", result, err)

    result, err = safeDiv(10, 0)
    fmt.Printf("10 / 0 = %d, err = %v\n", result, err)

    fmt.Println()

    // panicの伝播とrecoverの例
    level1()

    fmt.Println()

    // mustPositive
    fmt.Println("正の整数:", mustPositive(5))
    // mustPositive(-1) とするとpanicでプログラムがクラッシュします。
}
概要

Goのエラー処理は通常errorを戻り値で返す方式が推奨されますが、『panic()』はプログラムの継続が不可能な深刻な状況(初期化失敗・論理的に不正な状態)で使います。『recover()』は必ず『defer』内で使う必要があります。ライブラリ内でpanicが発生した場合、公開APIの境界でrecoverしてerrorとして返すのがGoのイディオムです。

通常のエラー処理にpanicを使うことは推奨されません。panicはコールスタック全体に影響を及ぼし、goroutine内のpanicは他のgoroutineに伝播しないため、各goroutineで個別にrecoverする必要があります。

deferとの組み合わせについては『defer』、通常のエラー処理は『error型 / errorsパッケージ』を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。