https://yuichi1004.hatenablog.com/entry/2016/11/01/195300

og-image-1500.png

今回は go のエラーハンドリングのパターンについて書いていきたいと思います。以下の Post とかなり被る部分があるので合わせて参考にしてください。

Error handling and Go - The Go Blog

Pattern 1: エラーを値として定義する

一番単純な方法としてエラーを値として定義してしまう方法があります。

var ErrNeedCake = errors.New("error - i need cake not this")
func Func1(name string) error {
        if name != "cake" {
                return ErrNeedCake
        }
        return nil
}

Func1() は引数 name をとり、引数がケーキでなければエラーを返すというシンプルな関数です。ここで ErrNeedCake というエラー構造体の値をパッケージ変数として定義しておきます。この方法を使うと、最もシンプルにエラーを定義することが出来ます。以下のようにすれば、呼び出し元のエラー判定にも使うことが出来ます。

func main() {
        if err := Func1("chocolate"); err != nil {
                if err == ErrNeedCake {
                        fmt.Printf("Hmm, he seems to need a cake\\n")
                } else {
                        fmt.Printf("Got unexpected error!\\n")
                        panic(err)
                }
        }
}

いっぽう、固定のエラーメッセージしか返すことが出来ないため、エラーの内容の詳細を伝えるとができません。

Pattern 2: 動的にエラー値を生成する

fmt パッケージには Printf と同様のフォーマットでお気軽に error 値を作ることができます。これを用いて動的にエラー値を作ることができます。

func Func2(name string) error {
    if name != "cake" {
        return fmt.Errorf("error - i need cake not %s", name)
    }
    return nil
}

おそらく最も簡単なエラーの返し方でしょう。また、メッセージを動的に生成できるのでより詳細な情報を返すことができます。

func main() {
    fmt.Println("\\n== Pattern 2: Creates ad-hok ==")
    if err := Func2("banana"); err != nil {
        fmt.Printf("Func2() got error: %v\\n", err)
    }
}

ただしこの方法はエラーの内容を表示する分には良いですが、分岐させることが難しいことです。簡単な使い捨てプログラムを書いているときや、クライアントが賢く分岐しないような局面では最もシンプルな戦略です。

Pattern 3: 独自エラー型を定義する

最も真面目でしっかりとエラーハンドリングをするには error interface を実装する独自のエラー型を定義することです。

type ErrNeedElse struct {
    Got  string
    Need string
}

func (e ErrNeedElse) Error() string {
    return fmt.Sprintf("error - i need %s not %s", e.Need, e.Got)
}

func Func3(name string) error {
    if name != "cake" {
        return ErrNeedElse{name, "cake"}
    }
    return nil
}