概要
github.com/pkg/errors の Wrap と Cause を使ったエラーハンドリングのサンプルを紹介します
Wrap と Cause 自体はエラーのラップとラップ前の大元のエラーを取得するための関数です
環境
- macOS 10.14.6
- golang 1.12.9
普通に構造体で判定する
まずは普通にエラーの構造体を作成して switch 文で判定してみます
errors パッケージも Wrap と Cause が使えない標準の errors パッケージになります
package main
import (
"errors"
"fmt"
"reflect"
)
type Error1 struct{}
type Error2 struct{}
func (e1 *Error1) Error() string {
return fmt.Sprintf("error1")
}
func (f *Error2) Error() string {
return fmt.Sprintf("error2")
}
func fetch(t int) error {
if t == 1 {
return &Error1{}
} else if t == 2 {
return &Error2{}
} else {
return errors.New("errorString")
}
}
func main() {
err := fetch(0) // 1 or 2 or other number
fmt.Println(reflect.TypeOf(err))
switch err.(type) {
case *Error1:
fmt.Println("error1 occured")
fmt.Println(err)
case *Error2:
fmt.Println("error2 occured")
fmt.Println(err)
default:
fmt.Println("errorString occured")
fmt.Println(err)
}
}
標準の errors パッケージのタイプを見ると *errors.errorString というタイプになっているようです
少し解説
Error() 関数を持った構造体を定義することで自作のエラー構造体として扱うことができます
fetch 関数では error タイプとして返却することができます
あとは受け取ったエラーを err.(type) でタイプ判定するだけです
一番オーソドックスな判定方法かなと思います
Wrap + Cause で大元の構造体を判定する
次に github.com/pkg/errors を使ってみます
これを使うことでエラーをラップしエラーが発生した経路を把握しやすくすることができます
package main
import (
"fmt"
"github.com/pkg/errors"
"reflect"
)
type Error1 struct{}
type Error2 struct{}
func (e1 *Error1) Error() string {
return fmt.Sprintf("error1")
}
func (f *Error2) Error() string {
return fmt.Sprintf("error2")
}
func fetch(t int) error {
if t == 1 {
e1 := &Error1{}
e2 := &Error2{}
return errors.Wrap(e1, e2.Error())
} else if t == 2 {
e2 := &Error2{}
e1 := &Error1{}
return errors.Wrap(e2, e1.Error())
} else {
return errors.New("fundamental")
}
}
func main() {
err := fetch(0) // 1 or 2 or other number
fmt.Println(reflect.TypeOf(err))
switch errors.Cause(err).(type) {
case *Error1:
fmt.Println("error1 occured")
fmt.Println(err)
case *Error2:
fmt.Println("error2 occured")
fmt.Println(err)
default:
fmt.Println("fundamental error occured")
fmt.Println(err)
}
}
github.com/pkg/errors パッケージを New すると errors.fundamental というタイプになりました
少し解説
errors.Wrap は既存のエラーにエラー文を追加することができる関数です
単純に末尾にエラー文が追加されます
大元のエラーのタイプは変わりません
大元のエラーのタイプを取り出す場合は errors.Cause を使います
これで大元のエラーを取り出してあとは type でタイプを参照すれば OK です
結局エラーの型を知らないとダメかな
エラーハンドリングする際は結局エラーの型を知らないと判定できないかなと思います
もしくはエラー文で分岐する方法もありますがその方法はほとんど見たことがありません
基本は error で返ってきて .(type) でキャストするとタイプがわかる感じなのでそれを使うのが簡単かなと思います
特にライブラリを使っている場合はこの方法になるかなと思います
どんなタイプのエラーが返ってくるはさすがにリファレンスやコードを読むしかないかなと思います
もしくはエラーを発生させられる環境があるのであれば サンプルにあるように fmt.Println(reflect.TypeOf(err)) でタイプを見ても良いかなと思います
もしくはインタフェースを実装するか
Cause とインタフェースを組み合わせてエラーを判定する方法もあるようです (参考)
自作のエラー構造体が特定のインタフェースを実装しているかどうかでエラーのタイプを判定します
0 件のコメント:
コメントを投稿