はじめに
はじめまして。Go初学者です。
Gopher道場#8や自身の勉強にて、気づいた学びを共有しています。
今回は、defer内でのエラーハンドリング方法と、名前付き戻り値の利用方針についてまとめました。
結論から
deferに渡した関数のreturnは無視されるので、deferに渡した関数のエラーは 名前付き戻り値 に代入して呼び出し元に戻す。
If the deferred function has any return values, they are discarded when the function completes. (See also the section on handling panics.)
deferred関数に戻り値がある場合は、関数が完了したときに破棄されます。
defer
is 何
いわゆる Finally 句の様なもので、関数が終了する前に呼ばれます。
defer キーワードの後に関数を渡すと、渡した順にスタックされていき、最後から順に実行されていきます。
func main() {
defer func() {
fmt.Println("defer 1")
}()
defer func() {
fmt.Println("defer 2")
}()
fmt.Println("hoge")
}
hoge
defer 2
defer 1
panicの場合でも、きちんと呼ばれてくれます。
func main() {
defer func() {
fmt.Println("defer 1")
}()
defer func() {
fmt.Println("defer 2")
}()
panic("panic!")
}
defer 2
defer 1
panic: panic!
goroutine 1 [running]:
main.main()
/tmp/sandbox481037905/prog.go:15 +0x68
Program exited: status 2.
以下の様に、ファイルのクローズ処理などによく用いられるそうです。
func main() {
file, _ := os.Create("hoge.txt")
defer file.Close()
// 書き込み処理
}
defer内でのreturnは無視される
下記の様な記述では、呼び出し元はerrを受け取る事ができません。
func main() {
if err := hoge(); err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
}
func hoge() error {
defer func() error {
return fmt.Errorf("error occured!")
}()
return nil
}
では、先ほどの例でファイル操作をしている場合などに、エラー判定を呼び出し元に返すにはどのようにしたらよいのか?
この際に、名前付き戻り値を利用します。
名前付き戻り値
is 何
Goでは戻り値に名称をつける事ができます。
また、付けられた名称は変数として関数内で利用する事が可能です。
名前付き戻り値を利用すると、returnのみでも名前を付けた変数が返却することができます。
func square(i int) (squared int) {
squared = i * i
return
}
良い所
戻り値に名前をつける事が出来るので、関数の利用者は作成者が何を返したいのかより明確になります。
IDEなどでも以下の様に表示されます。
ただ、そもそも関数名、引数名と引数の型、戻り値の型などを使って戻り値の意味が連想できないのはBadかなとは思いますが...
気になる所
私個人的には、returnで省略できる点については、ソースの可読性を下げる恐れがあると感じています。
なので、実際はreturn squaredの様に、明示的に記載することにしています。
deferに渡した関数内のエラーを呼び出し元に返す
本題です。
以下の様に、defer内で名前付き戻り値にエラーを設定することで、エラーを呼び出し元に返却する事が可能になります。
func main() {
if err := file("hoge"); err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
}
func file(fileName string) (err error) { // errorに名前をつける
file, _ := os.Create(fileName)
defer func() {
err = file.Close() // errに処理結果を代入
}()
// 書き込み処理
return nil
}
おわりに
基本的に名前付き戻り値は使わずに実装が可能ですが、上記のパターンの場合は使わずに返却することは難しいようでした。
名前付き戻り値の使い所はこういうパターンもあるんですね![]()
