隙あらば寝る

うぇぶのかいしゃではたらくえんじにあがかいています

go での error の返し方

go しばらく書いてると

someErr := errors.New("some error")

func SomeFunc(arg string) error {
    ...
    return someErr
}

func main(){
    if err := SomeFunc(a); err != nil{
        if err == someErr {
            log.Println(err)
        }
    }
}

のようなエラー処理はよくやる。 しかし、error に識別可能な情報があるのかどうか考えておく必要がある。

SomeFunc を数回呼ぶような場合、どの呼び出しでエラーが起きたのかは判断できない。

例えば、標準ライブラリの os.Open ではエラーには細かい情報が含まれる。

open /path/to: no such file or directory

関数を書くときにはその戻りの error に必要な情報を fmt.Errorf で埋めると良い。


しかし、動的な値を埋めると err == someErr のようなエラー内容による分岐ができなくなる。(strings.Contains 等でできなくもないが筋悪)

標準ライブラリではこのような問題を解決するのにエラー型を用いている。

エラー型を用いて最初の例を書き直すと以下のようになる。

type someErr error
someErr := errors.New("some error")

func SomeFunc(arg string) error {
    ...
    return someErr(fmt.Errorf("some error: %s", reason))
}

func main(){
    if err := SomeFunc(a); err != nil{
        if _, ok := err.(someErr); ok {
            log.Println(err)
        }
    }
}

os.IsNotExist 等はこのタイプ判別した結果を bool として返している。

こうすることでエラーには適切な情報を含めつつ、エラー判別も行える。

運用フェーズを考えてしっかりエラー処理を書こう。