Menu
Coddy logo textTech

エラーラッピング (fmt.Errorf)

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 54/107。

エラーがアプリケーションの複数の層を通過する場合、エラーがどこで発生したのかを把握することが重要になります。Go の fmt.Errorf 関数で %w 動詞を使うと、元のエラーを保持したままコンテキストを追加して、エラーをラップできます。

エラーのラップによって、エラーのチェーンが作成されます。各レイヤーでは、エラーが発生したときに何をしようとしていたのかについて情報を追加できます。

func ReadConfig(filename string) error {
    data, err := os.ReadFile(filename)
    if err != nil {
        return fmt.Errorf("reading config file: %w", err)
    }
    // データを処理...
    return nil
}

%w 動詞は特殊です。元のエラーを新しいエラーの内部にラップします。これは、エラーを文字列に変換するだけで元のエラーの同一性を失わせる %v とは異なります。

func LoadSettings() error {
    err := ReadConfig("settings.json")
    if err != nil {
        return fmt.Errorf("loading settings: %w", err)
    }
    return nil
}

LoadSettings が失敗すると、エラーメッセージには完全なチェーンが表示されます:"loading settings: reading config file: open settings.json: no such file or directory"。各層がコンテキストを追加するため、デバッグがはるかに容易になります。

ラップされたエラーには、元のエラーがその中に含まれたままです。次のレッスンでは、errors.Is()errors.As() を使って、これらのエラーをアンラップし、中身を確認する方法を学びます。

challenge icon

チャレンジ

簡単

複数のレイヤーにわたるエラーのラッピングを示す注文処理システムを構築しましょう。各レイヤーがエラーにコンテキストを追加する関数のチェーンを作成し、問題が正確にどこで発生したのかを簡単に追跡できるようにします。

コードを2つのファイルに分けて整理します。

  • orders.go: エラーが上位へ伝播する際にラップする複数のレイヤーを持つ注文処理ロジックを作成します。

    処理チェーンを形成する3つの関数を実装します。

    • ValidateItem(itemID string) error - itemID が "INVALID" の場合はメッセージ "item not found" のエラーを返し、それ以外の場合は nil を返します
    • ProcessOrder(orderID, itemID string) error - ValidateItem を呼び出します。エラーが返された場合は、コンテキスト "processing order [orderID]: %w" でラップします。それ以外の場合は nil を返します
    • SubmitOrder(customerName, orderID, itemID string) error - ProcessOrder を呼び出します。エラーが返された場合は、コンテキスト "submitting order for [customerName]: %w" でラップします。それ以外の場合は nil を返します

    各レイヤーでは、%w 動詞を指定した fmt.Errorf を使用して下位レイヤーのエラーをラップし、コンテキストのチェーンを構築します。

  • main.go: 入力から注文の詳細を読み取り、SubmitOrder を呼び出して、結果を表示します。エラーが発生した場合は、完全なエラーチェーンを出力します。成功した場合は、確認メッセージを出力します。

次の入力が提供されます。

  • 1行目: 顧客名
  • 2行目: 注文ID
  • 3行目: 商品ID

結果を出力します。

  • エラーが発生した場合: Error: [full error chain]
  • 成功した場合: Order [orderID] submitted successfully for [customerName]

たとえば、AliceORD-123INVALID が与えられた場合、出力は次のようになります。

Error: submitting order for Alice: processing order ORD-123: item not found

エラーメッセージに完全なチェーンが表示されていることに注目してください。最上位の送信試行から、注文処理を経て、実際の検証失敗に至るまで、問題を追跡できます。各レイヤーは %w を使用して独自のコンテキストを追加しています。

また、BobORD-456ITEM-001 が与えられた場合、出力は次のようになります。

Order ORD-456 submitted successfully for Bob

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 顧客名を読み取る
	scanner.Scan()
	customerName := scanner.Text()
	
	// 注文IDを読み取る
	scanner.Scan()
	orderID := scanner.Text()
	
	// アイテムIDを読み取る
	scanner.Scan()
	itemID := scanner.Text()
	
	// TODO: 入力値で SubmitOrder を呼び出す
	// TODO: エラーが発生した場合は次を出力する: Error: [full error chain]
	// TODO: If successful, print: Order [orderID] submitted successfully for [customerName]
	
	_ = customerName
	_ = orderID
	_ = itemID
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ