概要
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 件のコメント:
コメントを投稿