👏
もう迷わない!Go言語の「エラーを返す」ベストプラクティス5選
呼び出し元にエラーを返す方法
golangでは、関数内でエラーが発生した時に、エラー処理を関数内で完結させず、関数の呼び出し元にエラーを返す(return)するスタイルが一般的です。
returnの方法も様々なので、まとめてみました。
Go言語でerror型を返す主な方法の比較テーブル
方法 | 用途・特徴 | サンプルコード例 | 備考 |
---|---|---|---|
errors.New | 固定メッセージのエラーを返す | return errors.New("error message") |
シンプルで高速、標準的 |
fmt.Errorf | 変数や値を埋め込んだエラーメッセージを返す | return fmt.Errorf("error: %s", value) |
フォーマット文字列が使える |
fmt.Errorf + %w(ラップ) | 元のエラーを包んで、追加情報を持たせて返す | return fmt.Errorf("wrap: %w", err) |
Go1.13以降、errors.Is/Asで判定可能 |
グローバルエラー変数 | よく使うエラーを定数的に定義して返す | var ErrNotFound = errors.New("not found") |
比較や判定に便利 |
カスタムエラー型 | 独自の情報(コード等)を持つエラーを返す | type MyErr struct{...}; func (e MyErr) Error() string {...} |
errorインターフェースを実装 |
1. errors.New
- 固定のエラーメッセージを返したい場合に使います。
- 標準パッケージ
errors
をimportして利用します。
go
goimport "errors"
func foo() error {
return errors.New("something went wrong")
}
2. fmt.Errorf
- エラーメッセージに変数や値を埋め込みたい場合に使います。
- フォーマット文字列が使える点が特徴です。
go
goimport "fmt"
func foo(name string) error {
return fmt.Errorf("invalid name: %s", name)
}
3. エラーラップ(%w)で元のエラーを包む
- Go 1.13以降は
fmt.Errorf("...: %w", err)
で元のエラーをラップできます。 - 上位のコンテキストを追加しつつ、元のエラー情報も保持できます。
go
gofunc foo() error {
err := someFunc()
if err != nil {
return fmt.Errorf("foo failed: %w", err)
}
return nil
}
4. グローバルなエラー変数を定義して返す
- 汎用的なエラーや特定の状況を表すエラーを、パッケージレベルで変数として定義して返す方法です。
go
govar ErrNotFound = errors.New("not found")
func search(id int) (string, error) {
*// ...*
return "", ErrNotFound
}
5. カスタムエラー型を定義する
- より詳細な情報を持たせたい場合、自作のエラー型(struct)を作り、errorインターフェースを実装します。
go
gotype MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintf("code=%d, msg=%s", e.Code, e.Msg)
}
func foo() error {
return &MyError{Code: 404, Msg: "not found"}
}
errを受け取った呼び出し元はどうするのか
呼び出し元がmain関数アプリケーションのエントリポイントの場合
エラー発生時にlog.Fatalやos.Exitでプログラムを終了させることが一般的。
ただし、log.Fatalは内部でos.Exitを呼ぶため、deferによる後処理がスキップされる点に注意が必要です
上記以外のパッケージや関数
エラーをreturnで呼び出し元に伝播する
ライブラリや業務ロジックの関数でlog.Fatalやos.Exitを直接呼ぶのは避け、エラーをreturnで返し、最終的な判断はmain側で行います。。
Discussion