🛹

今goのエラーハンドリングを無難にしておく方法(2021.09現在)

に公開
8

Discussion

SpiegelSpiegel

標準 errors パッケージでもエラーのラッピングは既に実装されていますよ。 erros.Unwrap(), errors.Is(), errors.As() 関数でハンドリングします。また fmt.Errorf() 関数で %w 書式を使ってラップすることが可能です。 Go 2 に絡めるなら Generics 導入で errors.As() 関数のバリエーションが増える可能性はありますが,今のところは Go 2 も含めて現状が最終形です。

ただし標準パケージではスタックトレースは実装されていないので,この情報が必要なら自前で型を用意するか pkg/errors パッケージを使う,という住み分けになるんだと思います。 pkg/errors パッケージは放置されているわけではなく,設計がシンプルでバグが出る余地が少ないので改修頻度が少ないだけで,あちこちで使われてますよ。

pkg/errors の errors.(*withStack).Cause() 関数は(コードを見れば分かりますが) errors.(*withStack).Unwrap() 関数と同一のコードです。むしろ errors.(*withStack).Cause() 関数は自パッケージの後方互換性確保のために残されてると言った方がいいでしょうか。これから使うのであれば標準の erros.Unwrap(), errors.Is(), errors.As() 関数と組み合わせてハンドリングするのがいいと思います。

Nekoshita YukiNekoshita Yuki

ご指摘いただきありがとうございます!

標準 errors パッケージでもエラーのラッピングは既に実装されていますよ。

その通りですmm
最後の項目ではerrorのラップの話ではなくstacktraceの話をしているつもりでした
なので、最後の項目を以下のように変更しました。

結局標準errorsはラップする機能は取り込まれないの?
-> 結局標準errorsにstacktraceの機能は取り込まれないの?

Nekoshita YukiNekoshita Yuki

標準パケージではスタックトレースは実装されていないので,この情報が必要なら自前で型を用意するか pkg/errors パッケージを使う,という住み分けになるんだと思います

僕も同じ認識です!

pkg/errors パッケージは放置されているわけではなく,設計がシンプルでバグが出る余地が少ないので改修頻度が少ないだけで,あちこちで使われてますよ。

そうですね、僕の表現の仕方が放置してるっぽくなってしまっていたので修正しました

Nekoshita YukiNekoshita Yuki

pkg/errors の errors.(*withStack).Cause() 関数は(コードを見れば分かりますが) errors.(*withStack).Unwrap() 関数と同一のコードです。むしろ errors.(*withStack).Cause() 関数は自パッケージの後方互換性確保のために残されてると言った方がいいでしょうか。これから使うのであれば標準の erros.Unwrap(), errors.Is(), errors.As() 関数と組み合わせてハンドリングするのがいいと思います。

pkg/errors.Casue()pkg/errors.Unwrap() のコードは異なるようにみえます。

pkg/errorsを使ってエラーを発生させた場合のエラー原因の特定のために、pkg/errors.Cause()pkg/errors.Is()標準errors.Is()のどれを使っても問題なくハンドリングできるので、そこは好みで使い分けて良いと思います!

1点注意が必要なのが、pkg/errorsを使って発生させたエラーを pkg/errors.Unwrap()標準errors/Unwrap() する際の場合の挙動についてです。

https://github.com/pkg/errors/issues/223#issuecomment-587372942
詳細はこちらになりますが、pkg/errorsを使って発生させたエラーを2回 Unwrap()しないと1回分のUnwrapができません。
なので、pkg/errorsを使って発生させたエラーの原因を特定する場合には、 pkg/errors.Cause()pkg/errors.Is()標準errors.Is() を使うのがよさそうです。

SpiegelSpiegel

おー,なるほど。情報をありがとうございます。

pkg/errors の errors.Cause() と errors.(*withStack).Cause() および errors.Unwrap() と errors.(*withStack).Unwrap() がごっちゃの説明になってましたね。すみません。

tenntenntenntenn

プロポーザルを読み直しましたが、スタックトレースについてGo2でリリースされるとは書いてなさそうです。
そもそも、Go2というのは、後方互換が崩れるような変更を入れざる得ない時に生まれるバージョンなので、今の所予定はないと思います。

スタックトレースはGo1.13の開発時にxerrorsから輸入され、その後リリース前に消されたという経緯があります。
https://github.com/golang/go/issues/30468
https://go-review.googlesource.com/c/go/+/176997

Nekoshita YukiNekoshita Yuki

ご指摘、詳細な経緯をいただきありがとうございます!本文を修正させていただきました🙏