📑
【Go】(f *File) Close()のdefer処理をエラーハンドリングする
今回は、defer f.Close()
を例にして、defer処理内でのエラーハンドリングに関して書きます。
ファイルのClose処理
deferを使いファイルをクローズします。
f, _ := os.Create("test.json")
defer f.Close()
※記述の便宜上、エラーハンドリングを省略しています。
まずは、Close()の実装を見てみましょう。
関数を確認するとerrorを返却していることがわかります。
func (f *File) Close() error {
if f == nil {
return ErrInvalid
}
return f.file.close()
}
現状、 defer f.Close()
だけだと、Close()が返却するerrorは握り潰されています。
無名関数とnamed return valueを使いハンドリングする
無名関数とnamed return valueを使うことによって以下のように書けます。
f, _ := os.Create("test.json")
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = fmt.Errorf("defer close error: %v", closeErr)
}
}()
しかしこの書き方の場合、他の箇所で発生したエラーが上書きされてしまいます。
以下のコードを例にして解説をします。
まずは、上書きされない場合です。
func main() {
fmt.Println(createFile())
}
func createFile() (err error) {
f, _ := os.Create("test.json")
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = fmt.Errorf("defer close error: %v", closeErr)
}
}()
return fmt.Errorf("errorです")
}
実行結果
% go run main.go
errorです
createFile()
は、処理Xが失敗したと仮定して、fmt.Errorf("errorです")
を使いエラー詳細を返却しています。
ここで、f = nil
とし、意図的にdefer内部でエラーを発生させます。
defer func() {
f = nil
if closeErr := f.Close(); closeErr != nil {
err = fmt.Errorf("defer close error: %v", closeErr)
}
}()
実行結果
% go run main.go
defer close error: invalid argument
fmt.Errorf("errorです")
という情報が上書きされて、fmt.Errorf("defer close error: %v", closeErr)
のみが出力されました。
デバックを行うときに重要な情報であるfmt.Errorf("errorです")
が上書きされてしまいました。
そのため次のようにコードを修正します。
無名関数内でエラー情報をラップする
大元のエラー情報とf.Close()
のエラー情報を合わせて返してあげるように変更します。
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = fmt.Errorf("original error: %v, defer close error: %v", err, closeErr)
}
}()
実行結果
% go run main.go
original error: errorです, defer close error: invalid argument
無事に両方のエラー情報を返却することができました。
最終サンプルコード
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println(createFile())
}
func createFile() (err error) {
f, _ := os.Create("test.json")
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = fmt.Errorf("original error: %v, defer close error: %v", err, closeErr)
}
}()
return fmt.Errorf("errorです")
}
より良い書き方があればご意見お待ちしております。
Discussion