前書き
ファイルの存在チェックのためにエラー処理として「誤ったコード例 」のコードを書いていましたが、エラーがラップされるケースで正しく判定できない問題がありました。
具体的には、os.IsNotExist
関数を使用して存在チェックを行っていましたが、エラーにスタック情報を付与するためにerrors.WithStack
を用いると、元のエラーが隠蔽され、os.IsNotExist
では正しく判定できなくなります。
結論を先に書くと、現在はerrors.Is
関数という新しい関数がありこちらを使用します。
じゃあos.IsNotExist
関数はいつ使うの?という疑問が湧くと思いますが、こちらはerrors.Is
関数が実装される前に使用されていたものです。
今後実装する際にはerrors.Is
関数を使用するようにドキュメント
にも記載されています。
以下は以前に書いていた誤ったコード例です。
誤ったコード例
|
|
問題点の解説
-
エラーのラップと判定の問題
上記コードでは、os.ReadFile
で発生したエラーに対してerrors.WithStack(err)
を使い、エラーにスタックトレース情報を付与しています。
この場合、もともとのエラー(たとえばfs.ErrNotExist
)はラップされ、os.IsNotExist(err)
では直接比較できなくなってしまいます。
結果、存在しないファイルの場合でもos.IsNotExist
がfalse
を返し、想定外の動作をしてしまいます。 -
正しい判定方法について
この問題に対しては、エラーをラップした状態でも元のエラーと比較できるerrors.Is
関数を使用する方法が推奨されます。
もしくは、従来の方法としてはerrors.Cause(err)
で元のエラーを取り出してからos.IsNotExist
で判定する方法もありましたが、
errors.Is
を使えば一発で判定できるため、よりシンプルです。
以下は、上記誤った例を修正した正しい例です。
エラーのラップ処理後でも、errors.Is
関数を使うことで、元のエラー(ここではfs.ErrNotExist
)と正しく比較できます。
正しい例
|
|
解説
-
errors.Is
関数
errors.Is(err, fs.ErrNotExist)
を使うことで、エラーがerrors.WithStack
でラップされていても、内部に含まれる元のエラーと比較できるため、
ファイルが存在しない場合に正しく「存在しない」エラーを検出できます。 -
正しいエラーチェックの利点
この方法を採用することで、エラーにスタックトレース情報を追加しつつも、エラーチェックが確実に行えるため、
不測の事態に対する堅牢なエラーハンドリングが実現できます。
このように、ラップされたエラーも含めた比較が必要な場合は、errors.Is
を使用することが推奨されます。
まとめ
- 誤った例では、
errors.WithStack
でラップされたエラーに対してos.IsNotExist
を使ってしまい、
ファイルが存在しない場合の判定が正しく動作しませんでした。 - 正しくは、
errors.Is(err, fs.ErrNotExist)
を使用することで、ラップされたエラーでも正しく判定できます。
この例を通して、エラーのラップ処理とエラー判定の関係に注意しながら実装することが重要だと学んでいただければと思います。
実行環境
|
|