モジュールのレッスン (2/5)
慣用的なエラー処理
Go のエラー処理は明示的です。例外や try/catch はありません。失敗する可能性のあるすべての関数は、最後の値として error を返し、呼び出し元が決定します。この章では、堅牢な方法でエラーを生成、伝播、および検査するための慣用的なパターンを収集します。
黄金律
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}4 つの原則:
errorは最後の返品です。if err != nilを使用して すぐに確認してください。- 伝播する前に コンテキストを追加します (
fmt.Errorf+%wを使用)。 - エラーは決して無視しないでください**。処理するか、記録するか、返すかのいずれかを行ってください。
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)センチネルエラー
センチネル エラー は、呼び出し元が比較できるエクスポートされたグローバル変数 var ErrXxx = errors.New("...") です。
package store
var ErrNotFound = errors.New("not found")
func Get(key string) (string, error) {
if !exists(key) {
return "", ErrNotFound
}
// ...
}呼び出し元は errors.Is (== ではありません) を使用し、ラップ チェーンをたどります。
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
// handle 404
}標準的な例: io.EOF、sql.ErrNoRows、os.ErrNotExist。
カスタムタイプのエラー
構造化データ (HTTP コード、無効なフィールドなど) を運ぶには、タイプを定義します。
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}呼び出し元は errors.As を使用して、チェーンに沿って安全にダウンキャストします。
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("invalid field:", ve.Field)
}errors.As は、渡されたポインターに割り当て可能なエラーが見つかるまで、Unwrap() チェーンをたどります。
%w によるラッピング
fmt.Errorf と %w 動詞を組み合わせると、元のエラーを ラップして、errors.Is/errors.As 用に保持するエラーが生成されます。
if err := db.Query(); err != nil {
return fmt.Errorf("load users: %w", err)
}%w を使用しない (%v または %s を使用する) と、より豊富な文字列が得られますが、チェーンが 失われます。呼び出し元は特定のエラーを認識できなくなります。
%w のルール:
fmt.Errorf呼び出しごとに 1 つだけ (マルチラップにはerrors.Joinが必要です)。- 引数は文字列ではなく、
errorでなければなりません。 - コンテキストを追加します。元のメッセージを複製しないでください。
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"
// good: useful context
return fmt.Errorf("load config %s: %w", path, err)errors.Join (複数のエラーの場合)
Go 1.20 以降、errors.Join(errs...) は複数のエラーを 1 つに結合し、errors.Is/As ですべてのエラーを検出できるようにします。
var errs []error
for _, item := range items {
if err := process(item); err != nil {
errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}panic を使用する場合
予想されるエラーに対しては決して使用しないでください。 次の 3 つの場合のみ:
- 回復不可能なバグ: 「起こり得ない」箇所での不変式違反 (
panic("unreachable"))。 - スタンドアロン プログラムの
init()/main()で 初期化に失敗しました (重要な構成が欠落している → フェイル ファスト)。 - それを明示的に文書化するライブラリ API: 例:
regexp.MustCompile(パターンが無効な場合はパニックします。パターンはビルド時の定数であるため許容されます)。
それ以外はすべて error です。 panic をショートカットとして使用しないでください。上に伝播するため、構成が破壊され、defer Close() がスキップされ、呼び出し元に負担がかかります。
演習
fmt.Error で os.ReadFile のエラーをラップし、'config:' (コンパイル パス) のコンテキスト % を実行します。
3 回の試行後に解決策が利用可能になります
米国のエラーです。確認エラーです。メッセージ 'マンカ!' のエラーが見つかりませんでした。エラーが見つかりませんでした。タルカソで。
3 回の試行後に解決策が利用可能になります
Quando èidiomatico usare パニック in una libreria Go?
func F(x int) {
// ??? panic(...)
}