Programmingバックエンド開発者

Goのpackage error wrappingを介してエラー処理をどのように行うかについて説明してください:エラーラッピングとは何か、なぜそれが重要なのか、エラーのラッピングを正しく実装する方法は?

Hintsage AIアシスタントで面接を突破

回答。

問題の履歴

Go 1.13以前は、エラーは単純なインターフェースでした。エラーに追加のコンテキストを提供するために独自の型が作成されることが多かったですが、modern JavaやC#のような構造化されたラッピングのメカニズムはありませんでした。Go 1.13の登場により、fmt.Errorfを使用したエラーのラッピングと、根本原因の特定(errors.Is/errors.As)の標準的な方法が導入されました。

問題

複雑なアプリケーションでは、エラーがスタックの異なるレベルで発生する可能性があり、コードの深い部分からエラーを返す際にコンテキストを失わないことが重要です。さもなければ、デバッグが困難になり、どの時点でエラーが発生したのかを理解できなくなります。

解決策

Goは、原因を含む新しいエラーオブジェクトでエラーをラッピングすることを提案しています。これを行うには、fmt.Errorf%wを使用し、内部の原因を確認するにはerrorsパッケージのerrors.Iserrors.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.Iserrors.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への書き込みエラー")

利点:

  • 簡単で迅速。

欠点:

  • 実際には内層の"not found"エラーを区別できず、上位のビジネスロジックを壊す可能性があります。
  • スタックやエラーの発生源に関する情報の喪失。

ポジティブケース

%wでエラーをラッピングし、errors.Isで分析を使用しました:

if errors.Is(err, ErrNotFound) { return fmt.Errorf("サービスレベルのエラー:%w", err) }

利点:

  • どのレベルでも原因が正確に特定されます。
  • デバッグが簡単で、常に元のコンテキストが見えます。

欠点:

  • ラッピングがどのように機能するかを理解し、エラーを適切に記述する必要があります。