Go言語の勉強を始めたのですが、deferで引っかかってしまった部分があったのでまとめまてみました。
defer ステートメントは、 defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させるものです。
defer へ渡した関数の引数は、すぐに評価されますが、その関数自体は呼び出し元の関数がreturnするまで実行されません。
引用元:A Tour of Go
上記の通りdeferを使うことで、呼び出している関数がreturnするまで処理の実行を遅らせることができます。
例えばファイルを閉じたりチャネルをクローズするのに使われます。
サンプルコード
func main() {
file, _ := os.Open("./example.txt")
defer file.Close()
data := make([]byte, 100)
file.Read(data)
fmt.Println(string(data))
}
そして引っかかってしまった部分は
func main() {
var fruit string = "Orange"
defer fmt.Println(fruit)
fruit = "Apple"
}
結果:Orange
Appleが帰ってくると予想していたので、???となってしまいました。
実は引用文の2行目に非常に重要なことが書かれています。
defer へ渡した関数の引数は、すぐに評価されますが、その関数自体は呼び出し元の関数がreturnするまで実行されません。
つまりdeferへ渡した関数の引数は、deferが評価された時点で値が保持されてしまうということです。だからdeferの評価後に変数fruitの値を変更しても、deferでの実行には反映されていなかったのです。
他にもdeferを使ってpanicからrecoverするとき、
panic/recover
func main() {
defer func() {
s := recover()
fmt.Println(s,"ok")
}()
panic("panic!!!")
}
これだとrecoverできてokが出力されますが、
panic/recover
func main() {
panic("panic!!!")
defer func() {
s := recover()
fmt.Println(s,"ok")
}()
}
このようにpanicをdeferより前に書いてしまうとそのままプログラムが終了してしまいます。