Edited at

panicはともかくrecoverに使いどころはほとんどない

More than 5 years have passed since last update.

Goを書いていてrecoverを使うことはまずほとんどない。頻繁にrecoverを書いているとしたらなにかが間違っているのでプログラミングスタイルを見直すこと。


Goでのエラーハンドリング

Effective Goなどで説明されているように、Goではエラーは関数の返り値として返される。たとえばio.ReaderのRead関数は、読み込んだバイト数と、(nilかもしれない)エラーの2つの値を返す。Goでは基本的に、エラーは常にこういう通常の値としてハンドルするべきで、エラーの時のための特別な制御構造(try 〜 catch)のようなものを使うのは、利点より害のほうが多いという考え方をとっている。

(同じような考えで例外を使用禁止にしている大規模C++プログラムはいくつもある。たとえばChromiumなどはそうだ。LLVM/Clangもパフォーマンス上の問題で例外を使っていない。C++コンパイラを書いている人たちでも、自分たちのC++コンパイラでは例外を使わずにコードを書いている。)

panicは基本的に本当に例外的な状況で、エラーを表示してプロセスを終了するしかないようなときにだけ使うことが想定されている。

ではrecoverはどう使うかというと、panicが本当に例外的な状況で使われるものである以上、使いどころはない、ということになる。結論としては以上、なのだが……


それでもrecoverを使っているケース

稀にrecoverが(議論の余地はあるものの)正当な理由で使われることもある。ちょうどgolang-devでスレッドが立っていたのだが、標準ライブラリでは数カ所でrecoverが使われている。

encoding/gob

深くネストした再帰下降パーザから、入力にエラーがあった場合に一気にトップレベルの関数に脱出するためにpanic/recoverが使われている。recoverしたエラー値は呼び出し元に通常の返り値として返される。

encoding/json

同上

bytes/buffer

バッファサイズを2倍にしていくところで、メモリ割り当てに失敗したときユーザにエラー値を返すためにrecoverが使われている。

net/http

リクエストハンドラがpanicしたのをrecoverして単一のHTTPリクエストがプロセス全体を終了させないようにしている(これがよいことかどうかは議論の余地があるし、しかもドキュメントされていない)

Goの標準ライブラリは結構大きなGoのコードベースといっていいと思うが、recoverが使われている箇所は少ない。GoコアメンバのAndrew Gerrandさんもそのスレッドで、いままでにrecoverを書いたのは片手で数えられるくらいしかないといっているくらいなので、とりあえず使わなくてよいという認識で良さそうだ。

特に注意したいのが、パッケージのAPIとしてpanicを使うようなやり方だ。JavaのFileReaderみたいな「この関数はファイルがオープンできなかったときにXXXという例外を投げます」というようなAPIを用意するのは完全に間違っているのでやらないこと。