0Pricing
Go Academy · レッスン

panic、recover、defer

panicを使う場面と、適切にrecoverする方法

「panic、recover、defer」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。

panic とは

panic は現在の goroutine の通常の実行を停止し、defer された関数を実行しながら呼び出しスタックを巻き戻します。recover されなければプログラムはクラッシュします:

package main
import "fmt"

func mustPositive(n int) int {
    if n <= 0 {
        panic(fmt.Sprintf("mustPositive: got %d", n))
    }
    return n
}

func main() {
    fmt.Println(mustPositive(5))  // 5
    // fmt.Println(mustPositive(-1)) // panics!
}

panic を使う場面

panic は次の場合にのみ使用します:

  • 本当に回復不能なプログラマーエラー(不変条件の破綻)
  • バグを示す、起こり得ない条件
  • 起動時の失敗(設定の不足、初期化中の接続失敗)

想定される失敗条件には決して panic を使用せず、代わりに error を使用してください。

defer の基本

defer は、周囲の関数が戻るときに関数呼び出しを実行するように登録します。通常終了、エラー、panic のいずれの戻り方でも実行されます:

package main
import "fmt"

func main() {
    defer fmt.Println("third")
    defer fmt.Println("second")  // LIFO order
    defer fmt.Println("first")
    fmt.Println("running...")
    // Output:
    // running...
    // first
    // second
    // third
}

クリーンアップのための defer

defer の最も一般的な用途は、リソースが確実にクリーンアップされるようにすることです:

package main
import ("fmt"; "os")

func readFile(path string) (string, error) {
    f, err := os.Open(path)
    if err != nil {
        return "", fmt.Errorf("open: %w", err)
    }
    defer f.Close()  // always runs, even if we return early

    buf := make([]byte, 1024)
    n, _ := f.Read(buf)
    return string(buf[:n]), nil
}

recover — panic の捕捉

recover は panic を停止して、panic の値を返します。defer された関数の内部で直接呼び出す必要があります:

package main
import "fmt"

func safeDiv(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    return a / b, nil  // panics if b==0
}

func main() {
    r, err := safeDiv(10, 0)
    fmt.Println(r, err)
    // 0 recovered from panic: runtime error: integer divide by zero
}

recover は defer された関数内で呼び出す必要があります

recover は、defer された関数の内部で直接呼び出した場合にのみ機能します。それ以外の場所で呼び出すと nil を返します:

package main
import "fmt"

func wrong() {
    // recover() here does nothing — not in defer
}

func correct() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("caught:", r)
        }
    }()
    panic("oops")
}

func main() { correct() }

ライブラリコードでの panic

ライブラリコードでは、panic を呼び出し側にほとんど決して漏らしてはいけません。境界で panic をエラーに変換します:

package main
import ("fmt"; "runtime/debug")

func safeCall(fn func()) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
        }
    }()
    fn()
    return nil
}

func main() {
    err := safeCall(func() { panic("boom") })
    fmt.Println(err != nil) // true
}

defer の LIFO 順序とループの落とし穴

defer された呼び出しは、後入れ先出し(LIFO)の順序で実行されます。ループ内で defer するときは注意し、クロージャーまたはヘルパー関数を使用してください:

package main
import "fmt"

func processFiles(names []string) {
    for _, name := range names {
        name := name  // capture loop variable
        defer func() { fmt.Println("close:", name) }()
    }
}

func main() {
    processFiles([]string{"a.txt", "b.txt", "c.txt"})
    // Output (LIFO): close: c.txt, close: b.txt, close: a.txt
}

名前付き戻り値と defer

defer された関数は、名前付き戻り値を読み取って変更できます。これはエラーに情報を付加するときに便利です:

package main
import "fmt"

func doWork() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("doWork: %w", err)
        }
    }()
    return fmt.Errorf("something failed")
}

func main() {
    err := doWork()
    fmt.Println(err) // doWork: something failed
}

panic と error の使い分けガイド

panic と error の選択:

  • error を使用:想定される失敗(ファイルが見つからない、ネットワークタイムアウト、無効な入力)
  • panic を使用:プログラミングエラー(nil マップへのアクセス、範囲外アクセス)、起こり得ない状態、初期化の失敗
  • API の境界を越えて panic を使用しないでください。呼び出し側で処理できません
  • サーバーではクラッシュを防ぐため、goroutine の最上位で panic を recover します

確認問題

panic を捕捉するには、recover() をどこで呼び出す必要がありますか?

まとめ:panic、recover、defer

まとめ:

  • panic:スタックを巻き戻し、deferを実行し、回復されなければクラッシュします
  • defer:関数の終了時(正常終了、エラー、panicのいずれの場合も)に実行され、LIFO順になります
  • recover:panicを停止して値を返します。defer内で呼び出す必要があります
  • panicは、予期される失敗ではなくプログラマーのエラーに使用します
  • API境界ではpanicをエラーに変換します

よくある質問

「panic、recover、defer」レッスンは無料ですか?

はい。「panic、recover、defer」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。

「panic、recover、defer」で何を学びますか?

panicを使う場面と、適切にrecoverする方法 ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Go Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「panic、recover、defer」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このGo Academyレッスンでコードを書いて実行できますか?

はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. errorインターフェース
  2. カスタムエラーの作成
  3. エラーのラップとアンラップ
  4. panic、recover、defer
← Go Academyに戻る