【Golang】Go1.13 errors ライブラリによるerror値の取り扱い
error
のラッピング
Go 1.13 より、 fmt.Errorf
が error インタフェースを実装する値のラッピングをサポートした。この機能により、ある error 値を別の error に内包する事ができる。
func UpdateHoge(hoge: Hoge) error { err := app.UpdateHoge(hoge) if err != nil { return fmt.Errorf("Internal Server Error : %w", err) } }
%w
verb を format 内に含めることで、error 文字列内に第 2 引数以降にとる error 文字列を含めつつ、内部に error を保持させることができる。新規に作成した error がラップする error を取り出すには Unwrap()
関数を使用する。この関数は同じく Go 1.13 からサポートされるライブラリ errors
内に含まれる。
func Update(){ hoge := NewHoge() err := UpdateHoge(hoge) if err != nil { panic(errors.Unwrap(err)) } }
この errors.Unwrap(error)
関数は、引数に取る error が Unwrap()
を実装していればその関数が返す error 値を、そうでなければ nil
を返す。そのため、自前の error ラッピング構造体を定義したい場合、 Unwrap()
関数を定義する。
type AppError struct { err error msg string code ErrorCode } func (e AppError) Error() string { return e.msg } func (e AppError) Unwrap() error { return e.err }
fmt.Errorf()
のラッピングよって生成される error もこの Unwrap()
を定義している様子。
cerr := errors.New("child error") perr := fmt.Errorf("parent error : %w", cerr) fmt.Println(perr) // parent error : child error fmt.Println(errors.Unwrap(perr)) // child error fmt.Println(errors.Unwrap(cerr)) // <nil>
error の実装する Unwrap()
から取得できる error もまた、 Unwrap()
を実装しうる。この Unwrap()
の連鎖から取得できる一連の error を公式では err's chain と呼んでいる。この chain によって、複数の error 表現をコード内で取り回すことができる。
err's chain
内 error の有無チェック
errors
パッケージでは、err's chain のためのの比較用関数も提供する。
errors.Is()
は、第 1 引数の err's chain に第 2 引数の error が含まれるかどうかを bool で返す。
cerr := errors.New("child error") perr := fmt.Errorf("parent error : %w", cerr) fmt.Println(errors.Is(cerr, cerr)) // true fmt.Println(errors.Is(cerr, perr)) // false fmt.Println(errors.Is(perr, cerr)) // true
err's chain
に含まれる error の参照
errors.As()
関数は、第 1 引数にとる err's chain のうち、第 2 引数と一致する error 型の値があれば、その値で第 2 引数の参照先を置き換えて true
を返す。すなわち、第 2 引数は error ポインタ型となる。第 2 引数に他の型の値、及びポインタが渡されればパニックになり、nil が渡されれはfalse
を返す。
cerr := errors.New("child error") perr := fmt.Errorf("parent error : %w", cerr) fmt.Println(errors.As(perr, &cerr)) // true fmt.Println(errors.As(perr, nil)) // false
エラーチェーン中に特定の具象 error
が含まれれば特定の処理を実行しつつ具象 error を参照する、といった場合に有用そう。
// id : Int prod, err := GetProduct(id) var aerr AppError if errors.As(err, &aerr) { fmt.Errorf("error - %s", aerr) }