Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

fmt.Errorf() / %w

『fmt.Errorf()』は書式付きエラーメッセージを生成します。『%w』動詞を使うとエラーをラップでき、元のエラー情報を保持したまま文脈情報を付加できます。

構文
import "fmt"

// 書式付きエラーを生成します。
err := fmt.Errorf("フォーマット", 引数...)

// %w でエラーをラップします(errors.Is / errors.As で検索可能になります)。
wrapped := fmt.Errorf("文脈情報: %w", 元のエラー)

// %v でエラーメッセージを埋め込みます(ラップされないため errors.Is では検索できません)。
notWrapped := fmt.Errorf("文脈情報: %v", 元のエラー)
%w と %v の違い
動詞概要
%wエラーをラップします。生成されたエラーは『Unwrap()』メソッドを持ち、『errors.Is()』と『errors.As()』で元のエラーを再帰的に検索できます。
%vエラーのメッセージ文字列を埋め込むだけです。ラップは行われないため、『errors.Is()』での検索はできません。
エラーチェーン複数階層のラップが可能です。『errors.Is()』はチェーン全体を遡って一致するエラーを探します。
サンプルコード
package main

import (
    "errors"
    "fmt"
)

// 番兵エラーを定義します。
var ErrDatabase = errors.New("database error")

// 低レベルの関数です。
func queryDB(id int) error {
    if id <= 0 {
        return ErrDatabase
    }
    return nil
}

// 中間層の関数で文脈情報を付加します。
func getUser(id int) error {
    if err := queryDB(id); err != nil {
        // %w でラップすることでエラーチェーンを構築します。
        return fmt.Errorf("getUser(id=%d): %w", id, err)
    }
    return nil
}

// 上位層の関数でさらにラップします。
func handleRequest(id int) error {
    if err := getUser(id); err != nil {
        return fmt.Errorf("handleRequest: %w", err)
    }
    return nil
}

func main() {
    err := handleRequest(-1)
    if err != nil {
        // エラーメッセージ全体が表示されます。
        fmt.Println(err)
        // 『handleRequest: getUser(id=-1): database error』と出力されます。

        // errors.Is() はチェーンを遡って ErrDatabase を見つけます。
        if errors.Is(err, ErrDatabase) {
            fmt.Println("データベースエラーが原因です")
        }

        // errors.Unwrap() で一つずつアンラップします。
        inner := errors.Unwrap(err)
        fmt.Println(inner) // 『getUser(id=-1): database error』と出力されます。

        inner2 := errors.Unwrap(inner)
        fmt.Println(inner2) // 『database error』と出力されます。
    }

    // %v はラップしないため errors.Is() では見つかりません。
    notWrapped := fmt.Errorf("失敗: %v", ErrDatabase)
    fmt.Println(errors.Is(notWrapped, ErrDatabase)) // 『false』と出力されます。

    // %w はラップするため errors.Is() で見つかります。
    wrapped := fmt.Errorf("失敗: %w", ErrDatabase)
    fmt.Println(errors.Is(wrapped, ErrDatabase)) // 『true』と出力されます。
}
概要

『%w』によるエラーのラッピングは、呼び出し元に元のエラーの種類を判別させながら、文脈情報(どの関数で発生したか、どんな引数だったかなど)を付加するためのGoの標準的な手法です。エラーメッセージを読むだけでエラーの発生経路が把握できます。

『fmt.Errorf()』に『%w』は1つしか指定できません(Go 1.20以降は複数指定可能ですが、複雑になるため原則1つにとどめます)。

エラーの判定には『errors.Is() / errors.As()』を使用してください。独自のエラー型を使ったより詳細なエラー情報の付加は『カスタムエラー型』を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。