Menu
Coddy logo textTech

Panic、Defer、Recover

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 57/107。

Goはエラーを返すことを重視しますが、どうしても復旧できない状況もあります。このような場合のために、Goにはpanicdeferrecoverという、異常事態を処理する仕組みが用意されています。

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 を使用してください。

challenge icon

チャレンジ

簡単

panicdeferrecoverを使用して例外的な状況を適切に処理する、安全な除算 calculator を作成しましょう。この calculator は危険な操作を試み、panic を通常の error に変換して通常どおり処理できるようにします。

コードを2つのファイルに分けて整理します。

  • calculator.go: panic recovery を使った除算ロジックを作成します。

    2つの整数を受け取り、divisor がゼロの場合はメッセージ "division by zero" とともに panic する Divide function を実装します。それ以外の場合は、除算の integer 結果を返します。

    2つの整数を受け取り、integer の結果と error の両方を返す SafeDivide function を実装します。この function では、次の処理を行います。

    • anonymous function とともに defer を使用して、panic から recovery する
    • panic が recovery された場合、"calculation error: [panic message]" の format で error に convert する
    • 内部で Divide function を Call する
    • 成功した場合は結果と nil を返し、panic が recovery された場合はゼロと error を返す
  • 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 で表示されます。

たとえば、204 が与えられた場合、output は次のようになります。

Starting calculation
Result: 5
Releasing resources
Cleanup complete

また、100 が与えられた場合、output は次のようになります。

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete
REQUIRED OUTPUT FORMAT: [Your translated content here]

自分で試してみよう

package main

import (
	"fmt"
)

func main() {
	// 入力を読み取る
	var dividend, divisor int
	fmt.Scanln(&dividend)
	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]" のいずれかを出力する
	// エラーが返されたかどうかに基づいて
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ