問題の履歴:
Go 1.13以前は、エラーは単純なインターフェースでした。エラーに追加のコンテキストを提供するために独自の型が作成されることが多かったですが、modern JavaやC#のような構造化されたラッピングのメカニズムはありませんでした。Go 1.13の登場により、fmt.Errorfを使用したエラーのラッピングと、根本原因の特定(errors.Is/errors.As)の標準的な方法が導入されました。
問題:
複雑なアプリケーションでは、エラーがスタックの異なるレベルで発生する可能性があり、コードの深い部分からエラーを返す際にコンテキストを失わないことが重要です。さもなければ、デバッグが困難になり、どの時点でエラーが発生したのかを理解できなくなります。
解決策:
Goは、原因を含む新しいエラーオブジェクトでエラーをラッピングすることを提案しています。これを行うには、fmt.Errorfの%wを使用し、内部の原因を確認するにはerrorsパッケージのerrors.Isやerrors.Asを使用します。
コード例:
import ( "errors" "fmt" ) var ErrNotFound = errors.New("not found") func getData() error { return fmt.Errorf("service database: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("原因が特定されました:見つかりません") } else { fmt.Println("別のエラー:", err) } }
主な特徴:
fmt.Errorfを介してエラーをラッピングするための%wの使用。errors.Isとerrors.Asを使用してエラーの内部原因を特定する。fmt.Errorfを使用したエラーラッピングに必要なフォーマット指定子は何ですか?
答えは%wを使用することで、%vではなく、%wだけがアンラップをサポートします。
コード例:
fmt.Errorf("エラー:%w", err)
fmt.Errorfを使用せずに手動でエラーチェーンを作成し、errors.Isでそれを特定できますか?
いいえ、Unwrapインターフェースを実装する必要があります。そうでないと、標準関数はチェーンをアンラップしません。
インターフェースのコード例:
type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }
ラッピング後、内包されたエラーがnilの場合、エラーはnilの値を返しますか?
いいえ、内包されたエラーがnilの場合、ラッピングされたエラーもnilになりますが、正しく使用されている場合に限ります。しかし、エラーフィールドがnilのラッパー構造体を手動で作成した場合、nilにはなりません。これにより、チェック時に混乱が生じることがよくあります。
%wを使用せず、%vだけを使用する—チェーン分析の可能性が失われます。Unwrap()を実装しない。errors.Is/Asを使ってチェックせず、エラーを直接比較するだけにする。アプリケーション内で、各レベルで単に新しいエラーをテキストとともに返していました:
return errors.New("DBへの書き込みエラー")
利点:
欠点:
%wでエラーをラッピングし、errors.Isで分析を使用しました:
if errors.Is(err, ErrNotFound) { return fmt.Errorf("サービスレベルのエラー:%w", err) }
利点:
欠点: