Go: 独立した複数のエラーは errors.Join でまとめて返す
Go 1.20 以降の errors.Join(err1, err2, ...) は複数のエラーを 1 つにまとめる。Close とフラッシュの両方が失敗したときなどに、片方を握りつぶさずに返せる。
使う場面
defer f.Close()のエラーと本処理のエラーを両方返したいとき- 複数のバリデーションエラーや、複数の後始末の失敗をまとめて報告したいとき
- 以前は「最初のエラーだけ返す」か自前の multierror 型を使っていた場面
やり方
go
func writeFile(path string, data []byte) (err error) {
f, err := os.Create(path)
if err != nil {
return err
}
defer func() {
err = errors.Join(err, f.Close()) // nil は無視される
}()
_, err = f.Write(data)
return err
}
// 呼び出し側は従来どおり判定できる
if errors.Is(err, fs.ErrPermission) { ... }
var pe *fs.PathError
if errors.As(err, &pe) { ... }- 引数の nil は捨てられ、全部 nil なら
errors.Joinも nil を返す。だからerrors.Join(err, f.Close())をそのまま書ける。 errors.Is/errors.Asは、まとめた中のどれかに一致すれば true になる。Error()の文字列は各エラーを改行でつないだもの。
注意点
- ログで 1 行にしたい場合、改行が入る点に注意(構造化ログなら問題になりにくい)。
- 「A が失敗したので B を実行しなかった」のような因果関係があるなら、Join ではなく
fmt.Errorf("...: %w", err)でラップするほうが意味が伝わる。Join は互いに独立した失敗向け。 fmt.Errorfでも%wを複数書けば同様に複数ラップできる(Go 1.20+)。