Panic、Defer、Recover
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 57/107。
Goはエラーを返すことを重視しますが、どうしても復旧できない状況もあります。このような場合のために、Goにはpanic、defer、recoverという、異常事態を処理する仕組みが用意されています。
panic は通常の実行を直ちに停止します。これは範囲外のインデックスへのアクセスのようなプログラミングエラーのために予約されており、無効なユーザー入力のような予期されるエラーには使用しません。
func MustGetConfig(key string) string {
value, exists := config[key]
if !exists {
panic("missing required config: " + key)
}
return value
}defer は、周囲の function が通常どおり戻る場合でも panic によって戻る場合でも、その function が戻るときに実行する function をスケジュールします。deferred calls は逆順(後入れ先出し)で実行されます。
func ProcessFile() {
fmt.Println("Opening file")
defer fmt.Println("Closing file")
fmt.Println("Processing...")
// 出力: Opening file, Processing..., Closing file
}recover は panic を捕捉し、通常の実行に戻します。これは deferred function の内部でのみ機能します。
func SafeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic: %v", r)
}
}()
riskyOperation()
return nil
}このパターンは panic を通常の error に converts し、プログラムの処理を継続できるようにします。控えめに使用してください:予期される失敗のケースでは errors の返却を優先し、状態の破損や回復不能な状況のような、本当に例外的な状況に対してのみ panic/recover を使用してください。
チャレンジ
簡単panic、defer、recoverを使用して例外的な状況を適切に処理する、安全な除算 calculator を作成しましょう。この calculator は危険な操作を試み、panic を通常の error に変換して通常どおり処理できるようにします。
コードを2つのファイルに分けて整理します。
calculator.go: panic recovery を使った除算ロジックを作成します。2つの整数を受け取り、divisor がゼロの場合はメッセージ
"division by zero"とともに panic するDividefunction を実装します。それ以外の場合は、除算の integer 結果を返します。2つの整数を受け取り、integer の結果と error の両方を返す
SafeDividefunction を実装します。この function では、次の処理を行います。- anonymous function とともに
deferを使用して、panic から recovery する - panic が recovery された場合、
"calculation error: [panic message]"の format で error に convert する - 内部で
Dividefunction を Call する - 成功した場合は結果と
nilを返し、panic が recovery された場合はゼロと error を返す
- anonymous function とともに
main.go: input から2つの整数を Read し、SafeDivideを使用して安全に calculation を実行します。また、正しい order で cleanup message を Print してdeferの動作も示します。main function では、次の処理を行います。
- 開始時に
Starting calculationを Print する deferを使用してCleanup completeの Print を予約する- もう1つの
deferを使用してReleasing resourcesの Print を予約する - input values を使って
SafeDivideを Call する - 結果に応じて、
Result: [value]またはError: [error message]を Print する
- 開始時に
次の input が提供されます。
- Line 1: 最初の integer (dividend)
- Line 2: 2番目の integer (divisor)
deferred calls は reverse order(last-in, first-out)で execute されることに注意してください。そのため、cleanup messages は defer した order とは逆の order で表示されます。
たとえば、20 と 4 が与えられた場合、output は次のようになります。
Starting calculation
Result: 5
Releasing resources
Cleanup completeまた、10 と 0 が与えられた場合、output は次のようになります。
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete自分で試してみよう
package main
import (
"fmt"
)
func main() {
// 入力を読み取る
var dividend, divisor int
fmt.Scanln(÷nd)
fmt.Scanln(&divisor)
// 開始メッセージを出力する
fmt.Println("Starting calculation")
// TODO: Use defer to schedule "Cleanup complete" message
// TODO: Use defer to schedule "Releasing resources" message
// 覚えておくこと: 遅延呼び出しはLIFO順(後入れ先出し)で実行される
// TODO: 入力値でSafeDivideを呼び出す
// TODO: "Result: [value]" または "Error: [error message]" のいずれかを出力する
// エラーが返されたかどうかに基づいて
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ