ProgrammingGo開発者

Goにおけるエラー(errors)の処理の特徴は何ですか?エラーを正しく作成および処理する方法、カスタムエラータイプを実装する方法について教えてください。

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

回答

Goは、例外の代わりに明示的なエラーの返却を中心に設計されてきました。これにより、予測可能なコードが作成され、他の言語のtry/catch構文に見られる隠れた罠を避けることができます(例えば、JavaやC++など)。Goにおけるエラーは、Error()メソッドを実装するインターフェースであり、簡単なエラーや文脈を持つ複合エラー/ラップエラーを作成することができます。

問題は、エラーを適切に処理しない(例えば、_で無視する、デバッグ情報を付加せずにラップしない)場合や、エラーインターフェースに準拠しない「魔法の」エラーを生成する場合に発生します。また、パブリック関数からエラーを返し、各呼び出し時にエラーを確認することが重要です。

解決策は、標準的なアプローチを使用することです:

  • 常にエラーを関数の最後の値として返す。
  • errors.New、fmt.Errorfを使用してエラーを作成する。
  • カスタムエラー用に、自分のタイプを定義し、Error()を実装する。
  • 複雑なケースにはエラーラッパー(errors.Wrap)を使用して、コンテキストを失わないようにします。

コード例:

import ( "errors" "fmt" ) type NotFoundError struct { Resource string } func (e *NotFoundError) Error() string { return fmt.Sprintf("%sが見つかりません", e.Resource) } func GetUser(id int) (string, error) { if id != 1 { return "", &NotFoundError{"ユーザー"} } return "スティーブ", nil } func main() { user, err := GetUser(2) if err != nil { if nfe, ok := err.(*NotFoundError); ok { fmt.Println(nfe.Resource, "の問題") } else { fmt.Println("エラー:", err) } } fmt.Println("ユーザー:", user) }

重要な特徴:

  • エラーは常に明示的に返され、通常は関数の最後の引数です。
  • 複雑なエラーにはインターフェースエラーを実装する独自のタイプ(struct)を定義できます。
  • エラーをラップし、スタックトレースを保存するために、"errors"パッケージを使用できます(Go 1.13以降、errors.Isやerrors.Asを使用したエラーのチェーン処理をサポートしています)。

微妙な質問。

エラー処理で==で比較できるか、それとも特別なメソッドを使用する必要がありますか?

常にerrors.Is()を使用してエラーラッパーと比較することをお勧めします。そうしないと、エラーがラップされている場合に==による比較が機能しない可能性があります。

if errors.Is(err, os.ErrNotExist) { // ファイルの存在しない処理 }

カスタムエラーのために構造体タイプを実装する必要がありますか、それともerrors.New("...")を使うだけで十分ですか?

エラーメッセージだけが必要な場合は、errors.New()で十分です。コンテキスト(例えば、リソース名)を保持することが重要な場合は、Error()メソッドを持つstructを定義する方が良いでしょう。

関数が成功した場合、エラーをどのように正しく返すべきですか?

成功した場合は、常にnilをエラーとして返してください。

return result, nil

タイプエラーとアンチパターン

  • 戻り値のエラーを無視する(_を使用するか、処理をスキップする)
  • 入れ子になったエラーにも関わらず、==でのみエラーを比較する
  • エラーの代わりにハードコーディングされた文字列
  • エラーに追加のコンテキストがない

実生活の例

ネガティブケース

開発者が説明なしでエラーを返す関数を作成(単にerrors.New("失敗"))。ログを分析しても原因を特定できません。

利点:

  • 実装の速さ

欠点:

  • 情報の少ないエラー
  • デバッグの難しさ

ポジティブケース

開発者がカスタムエラータイプNotFoundErrorを定義し、詳細な説明を添えて返す。

利点:

  • ログのフィルタリングの利便性
  • エラーの検索と処理の容易さ

欠点:

  • 追加の構造体の説明をする必要がある