68
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

エラーを検査する

Last updated at Posted at 2015-05-08

NOTE: この記事は Inspecting Errors を翻訳したものです。
原文に従い、 Creative Commons ライセンスで公開します。

error インターフェイス型の値を返す関数の一般的な契約は、呼び出し側はその error をチェックする前にそれ以外のいかなる戻り値も利用してはいけないというものです。

多くのケースにおいて、関数が返した error 値は呼び出し元にとって不透明であるべきです。 error が nil かどうかチェックすることで呼び出しが成功したかどうかを知ることができ、そしてそれ以上のことはしません。

少ないケースにおいて、主にネットワークなどプロセス外の世界とやりとりする場合に、呼び出し側がエラーの種別を調べて、操作をリトライするべきかどうかを決める必要があります。

パッケージ作者に対して、呼び出し側がエラーの型を判別して利用できるように返すエラー型を公開して欲しいという要求がよくなされます。しかし、私はこの方法が幾つもの不幸な結果に繋がると信じています:

  • 公開されたエラー型はパッケージのAPIの表面積を大きくする
  • 実装を新しくした時、インターフェイスを定義したエラー型がもう実装にフィットしていないとしても、その型の値しか返せなくなる
  • パッケージを公開した後は、互換性を壊さずにエラー型を変更したり廃止することができない。これは脆いAPIにつながります

呼び出し側は、 error が特定の型かどうか検査することを、 Error() メソッドが返す文字列が特定のパターンにマッチするかどうか検査するより便利だと思うべきではありません。

代わりに、パッケージの作者と利用者が、実装と呼び出し側を密結合することなく関心事についてコミュニケーションする方法を提案します。

error を型ではなく振る舞いで検査する

error の値が特定の型であるかどうかを検査する代わりに、その値が特定の振る舞いを実装しているかどうかを検査してください。

この方法は、継承ベースの言語の is a [subtype of] よりも、 Go の暗黙のインターフェイスの has a 文化にうまくフィットします:

func isTimeout(err error) bool {
        type timeout interface {
                Timeout() bool
        }
        if te, ok := err.(timeout); ok {
                return te.Timeout()
        }
        return false
}

呼び出し側はこの isTimeout() を使って、 error が timeout インターフェイスを持っているか判別し、持っているならそのメソッドに問い合わせることで、その error がタイムアウトであるかどうかを判別することができます。
この方法なら実際の型やその error 値の出自について一切知る必要がありません。

(よくライブラリがエラーの発生したパスを注釈するために行う) エラーのラッピングもこの方法で可能です。
ラップされるエラーが実装しているインターフェイスを、ラップする側にも実装します。

これは非現実的な方法に思えるかもしれません。しかし、実際には一般的に使われるインターフェイスメソッドは比較的少なくて、 Timeout() boolTemporary() bool だけで多くのユースケースをカバーできます。

結論

error を型ではなく振る舞いで検査してください。

パッケージの作者は、もし一時的なエラーを返すのであれば、それに応じたメソッドを返すエラーの型に実装してください。もしエラー値をラップして返すのであれば、ラップするエラー値が実装しているインターフェイスをラッパーにも実装してください。

パッケージのユーザーは、エラーを検査する必要がある場合、エラーの型ではなく、エラーが期待する振る舞いをもっているかをインターフェイスを使って検査してください。パッケージの作者にパブリックなエラー型を追加するように依頼しないでください。かわりに Timeout() や Temporary() メソッドを適切に実装して一般的なインターフェイスに従うように依頼してください。

68
64
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?