kurumi-bioの雑記帳

プログラミング、パソコン、ペット、 犬、お出かけ

初心者のGo言語 その13 <fmt.Errorf>

こんにちは、kurumi-bioです。16回目のブログです。
前回に引き続きGo言語について発信します。

環境

OSバージョン:Windows11 Home 22H2
Go言語のバージョン:go version go1.19.4 windows/amd64

fmt.Errorf

func Errorf(format string, a ...any) error

Errorf は、フォーマット指定子に従ってフォーマットし、エラーを満たす値として文字列を返します。

フォーマット指定子にエラー オペランドを持つ %w 動詞が含まれている場合、返されるエラーは、オペランドを返す Unwrap メソッドを実装します。複数の %w 動詞を含めたり、エラー インターフェイスを実装しないオペランドを指定したりすることは無効です。それ以外の場合、%w 動詞は %v の同義語です。

<サンプルコード>ErrorfSample.go

package main

import "fmt"

func main() {
    err := fmt.Errorf("ERROR-%d:%s", 100, "File not Found.")
    fmt.Println(err.Error())
}

<実行結果>

エラー文字列をエラー番号(100)とエラー内容"File not Found."で生成しました。 error型の変数が作成されましたが、string型と大きな差が無いように感じます。

では、書式指定に%wを含めるとどうなるのか試してみます。
<サンプルコード>ErrorfSampleK1.go

package main

import "fmt"

func main() {
    err := fmt.Errorf("ERROR-%d:%s", 100, "File not Found.")
    fmt.Println(err.Error())

    err2 := fmt.Errorf("[ERROR2]Read Faild : %s", err)
    fmt.Println(err2.Error())

    err3 := fmt.Errorf("[ERROR3]Read Faild : %w", err)
    fmt.Println(err3.Error())
}

<実行結果>

%wは、エラーオペランドを持つと言うことですので、
一階層上のエラー(err2変数、err3変数)に先ほどのエラー(err変数)を含めてみました。
err2とerr3の表示結果は同じで"ファイルが無かったから、読み込めなかった"という事がわかります。

では、返されるエラーは、オペランドを返す Unwrap メソッドを実装します。
は何を意味しているのでしょうか。

公式ページでUnwrap を検索するとerrorsパッケージに含まれている関数という事がわかります。

では、errors.Unwrap関数を使って違いがでるか試してみます。

<サンプルコード>ErrorfSampleK2.go

package main

import "fmt"
import "errors"

func main() {
    err := fmt.Errorf("ERROR-%d:%s", 100, "File not Found.")

    err2 := fmt.Errorf("[ERROR2]Read Faild : %s", err)
    fmt.Println("err2=", errors.Unwrap(err2))

    err3 := fmt.Errorf("[ERROR3]Read Faild : %w", err)
    fmt.Println("err3=", errors.Unwrap(err3))
}

<実行結果>

err2変数は、%sでerrを含ませたためerrors.Unwrapの値がになっています。
err3変数は、%wでerrを含ませたためerrors.Unwrapの値がerrの値になっています。
ですので、fmt.Errorfで%wを使うとerrorのネストを作成することが可能になります。

例えば、複数のerrorを含ませて順に出力していくサンプルプログラムを記載します。
<サンプルコード>ErrorfSampleK3.go

package main

import "fmt"
import "errors"

func main() {
    err1 := fmt.Errorf("%s", "err1")
    err2 := fmt.Errorf("err2:%w", err1)
    err3 := fmt.Errorf("err3:%w", err2)
    err4 := fmt.Errorf("err4:%w", err3)

    fmt.Println("err4=", err4)

    e := err4
    for {
        if e == nil {
            break
        }
        fmt.Println("e=", e)
        e = errors.Unwrap(e)
    }
}

<実行結果>

このようにerr4変数には、err1からerr3までのerrorが含まれており、
errors.Unwrap関数で取り出すことが可能です。

go1.20との差異

<2023年2月24日 追記>
go1.20で機能変更がありました。詳細は下記を参照してください。 kurumi-bio.hatenablog.com