web-dev-qa-db-ja.com

Golangでパニックをキャッチ

次のコードでは、ファイル引数が指定されていない場合、行9 panic: runtime error: index out of range 予想通り。

このパニックを「キャッチ」して、何かを渡すときに直接処理する方法(os.Args[1])パニックの原因は? try = catch in PHPまたはtry/except in Pythonに似ています。

ここでStackOverflowを検索しましたが、これに答えるようなものは見つかりませんでした。

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }
    fmt.Printf("%s", file)
}
43
Luke B

GoはPythonではありません。使用する前に引数を適切に確認する必要があります。

func main() {
    if len(os.Args) != 2 {
         fmt.Printf("usage: %s [filename]\n", os.Args[0])
         os.Exit(1)
    }
    file, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", file)
}
41
OneOfOne

パニックプログラムは、組み込みのrecover()関数で recover を実行できます。

recover関数を使用すると、プログラムはパニック状態のゴルーチンの動作を管理できます。関数GDを呼び出す関数recoverを延期し、Gが実行されている同じゴルーチンの関数でパニックが発生したとします。遅延関数の実行がDに達すると、Drecoverへの呼び出しの戻り値は、panicの呼び出しに渡される値になります。新しいDを開始せずにpanicが正常に戻った場合、パニックシーケンスは停止します。その場合、Gpanicの呼び出しの間に呼び出された関数の状態は破棄され、通常の実行が再開されます。 Gの前にDによって遅延された関数はすべて実行され、Gの実行は呼び出し元に戻ることで終了します。

次の条件のいずれかが当てはまる場合、recoverの戻り値はnilです。

  • panicの引数はnilでした。
  • ゴルーチンはパニック状態ではありません。
  • recoverは、遅延関数によって直接呼び出されませんでした。

これを使用する方法の例を次に示します。

// access buf[i] and return an error if that fails.
func PanicExample(buf []int, i int) (x int, err error) {
    defer func() {
        // recover from panic if one occured. Set err to nil otherwise.
        if (recover() != nil) {
            err = errors.New("array index out of bounds")
        }
    }()

    x = buf[i]
}

多くの場合、パニックは適切な解決策ではないことに注意してください。 Goパラダイムは、エラーを明示的にチェックすることです。プログラムは、通常のプログラム実行中にパニックが発生する状況が発生しない場合にのみパニックを起こすべきです。たとえば、ファイルを開くことができないということは、起こりうることであり、メモリ不足でパニックを引き起こすことはないはずです。それにもかかわらず、このメカニズムは、これらのケースをキャッチし、おそらく正常にシャットダウンできるように存在します。

87
fuz

最初:これをしたくないでしょう。 try-catch-styleエラー処理はエラー処理ではありません。 Goでは、最初にlen(os.Args)をチェックし、存在する場合にのみ要素1にアクセスします。

まれなケースでは、パニックをキャッチする必要があります(そして、ケースはnotそれらの1つです!)deferrecoverと組み合わせて使用​​します。 http://golang.org/doc/effective_go.html#recover を参照してください

14
Volker

一部のGolang公式パッケージは、panic/defer + recoverthrow/catchとして使用しますが、大規模な呼び出しスタックを解く必要がある場合のみです。 Golangのjsonパッケージの使用panic/defer + recover as throw/catchが最もエレガントなソリューションです。

from http://blog.golang.org/defer-panic-and-recover

パニックと回復の実際の例については、Go標準ライブラリのjson packageを参照してください。 JSONエンコードされたデータを一連の再帰関数でデコードします。誤った形式のJSONが検出されると、パーサーを呼び出してスタックをトップレベルの関数呼び出しに戻し、パニックから回復します適切なエラー値を返します(decode.goのdecodeStateタイプの「error」および「unmarshal」メソッドを参照)。

検索する d.error( at http://golang.org/src/encoding/json/decode.go

あなたの例では、「イディオマティック」ソリューションは、他のソリューションが指摘しているように、使用する前にパラメータをチェックすることです。

しかし、anythingをキャッチしたい/必要な場合は、次のことができます:

package main

import (
    "fmt"
    "os"
)

func main() {

    defer func() { //catch or finally
        if err := recover(); err != nil { //catch
            fmt.Fprintf(os.Stderr, "Exception: %v\n", err)
            os.Exit(1)
        }
    }()

    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }

    fmt.Printf("%s", file)
}
13
Lucio M. Tato

Recoverを使用すると、プロセスを停止せずにパニックを管理できます。 deferを使用して関数でrecoverを呼び出すと、呼び出し元の関数に実行が戻ります。 Recoverは2つの値を返します。1つはブール値で、もう1つは回復するインターフェイスです。型アサーションを使用すると、基になるエラー値を取得できます。recoverを使用して基になるエラーを出力することもできます。

defer func() {
    if r := recover(); r != nil {
        var ok bool
        err, ok = r.(error)
        if !ok {
            err = fmt.Errorf("pkg: %v", r)
        }
    }
}()
3
Himanshu

テストケースでパニックをキャッチする必要がありました。ここにリダイレクトされました。

func.go

var errUnexpectedClose = errors.New("Unexpected Close")
func closeTransaction(a bool) {
    if a == true {
        panic(errUnexpectedClose)
    }
}

func_test.go

func TestExpectedPanic() {
    got := panicValue(func() { closeTransaction(true) })
    a, ok := got.(error)
    if a != errUnexpectedClose || !ok {
        t.Error("Expected ", errUnexpectedClose.Error())
    }
}

func panicValue(fn func()) (recovered interface{}) {
    defer func() {
        recovered = recover()
    }()
    fn()
    return
}

https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 (VonCからの参照)から使用

2
Sairam

パニックExecutionエラー(境界外の配列のインデックス付けを試みるなど)エラーの回復処理は、go 1.7の後 問題14965

CL 21214 および its test を参照してください:

ランタイム:実行エラーのパニック値にErrorインターフェイスを実装させる

実行パニックに、文字列のパニックの代わりに、 実行時パニック(仕様) で義務付けられているエラーを実装させます。

パニックエラーを回復すると、次のことができるようになります。

if _, ok := recovered.(runtime.Error); !ok {

これはまだ評価中で、 Dave Cheney として。言及:

私は人々が現在何をしているのか分かりませんが、私のPOVからこれは長い間壊れており、誰も文句を言っていないので、彼らは壊れた行動に明示的に依存しているか、誰も気にしません。どちらにしても、この変更を避けることは良い考えだと思います。

1
VonC