Go公式WikiのPanicAndRecoverの日本語意訳になります。
(元記事の最終更新日: 2017/05/10 rev.7)
パニック(panic
)とリカバー(recover
)についての例が載っています。
Panic And Recover
パニック
panic
とrecover
関数は、panic
はプログラムのスタックを巻き戻し始め、recover
はそれを止めることができるという点で、他の言語での例外(exception
)やtry/catch
と同じように動作します。
遅延実行関数は、スタックが巻き戻されるときにも実行されます。
もしrecover
が遅延実行関数のような場所で呼ばれた場合、スタックは巻き戻すのをやめ、recover
はpanic
に渡した値を(interface{}
として)返却します。
配列やスライスの範囲外のインデックス化のような特別な状況では、ランタイムはpanic
を起こします。もしpanic
が実行中のゴルーチン外へスタックを巻き戻した場合(例: main関数や最上位の関数からの復帰に失敗した場合)、プログラムは全ての実行中のゴルーチンのスタックトレースとともに終了します。
panic
は別のゴルーチンでrecover
することができません。
訳注: 「スタックを巻き戻す」はpanicが発生すると、それ以降の行は実行されず直ちに呼び出し元へ処理を戻す(スタックを戻す)動作を意味しています。
パッケージでの使用方法
慣例では、明示的なpanic()
がパッケージの境界を超えるべきではありません。
呼び出し元へエラーの状態を示すには、エラー値を返却することによってなされるべきです。
しかしながら、パッケージ内、特に深くネストされた非公開関数の呼び出しでは、panicを使い呼び出し元関数のエラーに変換する必要があるエラーの状態を示すと便利です(そして可読性も向上します)。
以下はネストされた関数と公開関数とが、エラー上のパニックの関係を介してやりとりする方法の明白に考案された例になります。
// ParseError は単語から整数へと変換する際のエラーを示します.
type ParseError struct {
Index int // スペース区切りの単語リストへのインデックス.
Word string // パースエラーで生成された単語.
Error error // もし存在する場合は、このエラーを引き起こした元のエラー.
}
// String は人間に読みやすいエラーメッセージを返却します.
func (e *ParseError) String() string {
return fmt.Sprintf("pkg: %q をintにするパースエラー", e.Word)
}
// Parse はスペース区切りの整数文字列を、整数型リストへとパースします.
func Parse(input string) (numbers []int, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
fields := strings.Fields(input)
numbers = fields2numbers(fields)
return
}
func fields2numbers(fields []string) (numbers []int) {
if len(fields) == 0 {
panic("パースする単語がありません")
}
for idx, field := range fields {
num, err := strconv.Atoi(field)
if err != nil {
panic(&ParseError{idx, field, err})
}
numbers = append(numbers, num)
}
return
}
訳注: 上記では、公開関数の
Parse()
が外部のパッケージから呼び出されます。
その他の非公開関数(fields2numbers()
)が起こしたpanic
は、このParse()
の中でrecover
を使い受け止めて、panic
ではなく通常のエラーとして呼び出し元へ返却しています。
動作の説明のため、次のmain関数をよく考えてみてください。
func main() {
var examples = []string{
"1 2 3 4 5",
"100 50 25 12.5 6.25",
"2 + 2 = 4",
"1番目のクラス",
"",
}
for _, ex := range examples {
fmt.Printf("パース %q:\n ", ex)
nums, err := Parse(ex)
if err != nil {
fmt.Println(err)
continue
}
fmt.Println(nums)
}
}
訳注: Go PlayGroundで確認
参考
- Defer, Panic and Recover
- https://golang.org/ref/spec#Handling_panics
- https://golang.org/ref/spec#Run_time_panics