Go でパニックをキャッチ

Jay Singh 2023年6月20日
  1. Go でパニックをキャッチ
  2. Go でパニックを回復する
Go でパニックをキャッチ

エラーのようなパニックは、実行時に発生します。 つまり、Go プログラムで予期しない状況が発生するとパニックが発生し、実行が終了します。

範囲外の配列アクセスなど、特定の状態が発生すると、実行時にパニックが発生する可能性があります。 これらの典型的なアクションには、制限を超えた配列のインデックス作成、型テストの実施、nil ポインターでのメソッドの実行、mutex の誤った使用、閉じたチャネルとの対話の試みなどがあります。

これらのシナリオのほとんどは、ソフトウェアのビルド中にコンパイラが検出できないプログラミング エラーから発生します。 パニックは問題を修正するための重要な情報を提供するため、開発者は頻繁にパニックを利用して、開発中に間違いを犯したことを示します。

Golang でパニックをキャッチする例をいくつか見てみましょう。

Go でパニックをキャッチ

以下のプログラムは人のフルネームを表示します。 fullName 関数は人の完全な名前を出力します。

このメソッドは、firstName および lastName ポインタが null かどうかを判断します。 nil の場合、関数は適切なメッセージで panic を呼び出します。

例 1:

package main

import (
    "fmt"
)

func fullName(firstName *string, lastName *string) {
    if firstName == nil {
        panic("runtime error: first name cannot be nil")
    }
    if lastName == nil {
        panic("runtime error: last name cannot be nil")
    }
    fmt.Printf("%s %s\n", *firstName, *lastName)
    fmt.Println("returned normally from fullName")
}

func main() {
    firstName := "Jay"
    fullName(&firstName, nil)
    fmt.Println("returned normally from main")
}

出力:

panic: runtime error: last name cannot be nil

goroutine 1 [running]:
main.fullName(0x405058?, 0xc0000a2f70?)
    /tmp/sandbox3339134150/prog.go:12 +0x114
main.main()
    /tmp/sandbox3339134150/prog.go:20 +0x35

以下のコードは、employee 関数が main 関数から呼び出されることを示しています。 従業員の 名前年齢 を入力として送信します。

従業員の年齢が定年を超えると、従業員機能はパニック モードになります。

例 2:

package main

func employee(name *string, age int) {
    if age > 65 {
        panic("Age cannot be greater than retirement age")
    }
}
func main() {
    empName := "Jay"
    age := 73

    employee(&empName, age)
}

出力:

panic: Age cannot be greater than retirement age

goroutine 1 [running]:
main.employee(...)
    /tmp/sandbox4109873090/prog.go:5
main.main()
    /tmp/sandbox4109873090/prog.go:12 +0x27
exit status 2

Go でパニックを回復する

recover 関数は、エラーの値を使用して、パニックが発生したかどうかを判断します。 パニック関数の入力は空のインターフェイスであるため、任意の型にすることができます。

空のインターフェイスを含むすべてのインターフェイス タイプは、nil のゼロ値を持ちます。 この例に見られるように、パニックへの入力として nil を使用しないように注意する必要があります。

例:

package main
import (
    "fmt"
    "log"
)
func main() {
    divideByZero()
    fmt.Println("Hey, we survived dividing by zero!")

}
func divideByZero() {
    defer func() {
        if err := recover(); err != nil {
            log.Println("panic occurred:", err)
        }
    }()
    fmt.Println(divide(1, 0))
}
func divide(a, b int) int {
    if b == 0 {
        panic(nil)
    }
    return a / b
}

出力:

Hey, we survived dividing by zero!