この「一部のエラーハンドリングの無視」はGoの慣習に対し反しているのでは?という疑問はGoを学ぶ上で多くの人が通過するところです。
多くのCloseメソッドの役割は「リソースの後始末」です。「借りたものを返す」イメージです。
そしてここでエラーが返ることがあるのか?というとかなりレアなケースで存在しますが、それは「借りたものを返す」だけで済まないハイレベルな要求を満たすためだったりします。
(もちろんコードを追いかけると絶対エラーにならない実装もあります)
実際にいろんなCloseメソッドの実装を見て回ってみると良いでしょう。
http.Responseの内部で使っているcloseBodyという処理を見てみるとそもそもエラーを見ていません。
https://github.com/golang/go/blob/master/src/net/http/response.go#L336-L340
つまり、Response.Body.Closeはほとんどのケースでエラーの返らないメソッドだからエラーを無視しているのです。
レアケースな例としては「バッファ付きのio.WriteCloser」に該当するものですね。
例えばos.Createなどが返すファイル構造体などがこれに該当しますが、この*File.Close
の処理には2つの役割があります。
- バッファに残りがあればそれを書き出す
- その後リソースを返却する
後者はエラーが発生するときはOSのトラブル等かなり超レアな致命的な場合のみで、通常エラーになることはありません。前者はそれなりにエラーが発生することは起こりえます(書き出し先USBメモリのアンマウントなど)。
なのでファイルの書き出し系では以下のように書くことが推奨されています。
go
1fp, err := os.Create("sample.txt")
2if err != nil { /* error handling */ }
3defer fp.Close()
4if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ }
5if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ }
6if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ }
7if err := fp.Sync(); err != nil { /* error handling */ }
Syncにより、バッファの内容をファイルに書ききってしまうという処理をあらかじめすましておきます。
そうすると、defer fp.Close()
がエラーを返す状況はOSのトラブル等致命的な状況のみになります。
では致命的なエラーを見ないといけないのでは?
という疑問が続くのはごもっともなんですが、その状況が起きたとき、それを検出したりユーザーにお知らせする役割はアプリケーションではなく、OSの仕事です。
Goアプリケーションがその致命的なエラーを検出したとしても、その後それをログに残したり、コンソールに出力できるのかというとそれも期待できないような状況が「致命的な」状態なのです。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/06/23 13:56
2022/06/27 04:52
2022/06/27 08:50
2022/06/28 05:21