前回はエラー処理の基本を確認しました。
その中でエラーのラッピングとカスタムエラーについても簡単に説明しました。今回はカスタムエラーのラッピングについて確認します。
fmt.Errorf関数でエラーのラッピング
前回のエラー処理の基本でも動作を確認しましたが、fmt.Errorf関数を使用してエラーをラッピングします。errors.Is関数は簡単に言うと受け取ったエラーが想定されるエラーかどうかを判定してくれる関数と思って下さい。
package main
import (
"errors"
"fmt"
)
type MyError struct {
}
func (em MyError) Error() string {
return "俺のエラー!!"
}
func main() {
// エラーラップする
reErr := fmt.Errorf("%w", MyError{})
// 想定通りのエラーである事を確認する
if errors.Is(reErr, MyError{}) {
fmt.Println("俺のエラーがあった!!") // 俺のエラーがあった!! が表示される。
} else {
fmt.Println("俺のエラーはない!!")
}
}
カスタムエラーでラッピング
以下のようにカスタムエラーに他のエラーを保持したい時もあります。しかし、結果は保持しているエラーを確認できません。
package main
import (
"errors"
"fmt"
)
type MyError struct {
err error
}
type orgError struct {
}
func (em MyError) Error() string {
return "俺のエラー!!"
}
func (em orgError) Error() string {
return "オリジナルエラー!!"
}
func main() {
// オリジナルエラーを俺のエラーでラッピングする
reErr := MyError{err: orgError{}}
// オリジナルのエラーを確認する
if errors.Is(reErr, MyError{}) {
fmt.Println("俺のエラーがあった!!")
} else {
fmt.Println("俺のエラーはない!!") // 俺のエラーはない!! が表示される
}
}
fmt.Errorf関数でラッピングしたエラーはerrors.Is関数で検知できましたが、カスタムエラーでラッピングしたエラーは検知できませんでした。
解決
errors.Is関数の内容
ざっくり言うとerrors.Is関数は第一引数に他から受け取ったエラー、第二引数に想定するエラーを指定します。errors.Is関数は内部で第一引数のエラーに対してUnwrap関数を使用して、第二引数のエラーと比較しています。
fmt.Errorf関数が返すもの
fmt.Errorf関数が返すエラーは、Unwrap関数を実装しています。なのでerrors.Is関数はラッピングされたエラーを検知できます。
具体的な対策
と言うことでerrors.Is関数の第一引数に渡すエラーにUnwrap関数を実装します。
func (em MyError) Unwrap() error {
return em.err
}
全体的なコードは以下の通りです。
package main
import (
"errors"
"fmt"
)
type MyError struct {
err error
}
type orgError struct {
}
func (em MyError) Error() string {
return "俺のエラー!!"
}
// 追加
func (em MyError) Unwrap() error {
return em.err
}
func (em orgError) Error() string {
return "その他エラー!!"
}
func main() {
// エラーをラップする
reErr := MyError{err: orgError{}}
// オリジナルエラーの存在を確認する
if errors.Is(reErr, orgError{}) {
fmt.Println("その他エラーがあった!!") // その他エラーがあった!! が表示される
} else {
fmt.Println("その他エラーはない!!")
}
}