【Go】「deferは関数終了時に実行される」その認識、大丈夫？

# はじめに
* Goの`defer`について「呼び出し元の関数の終了時に実行されるんだな！」と軽く理解していませんか？(私もその一人でした、偉そうなタイトルですいません...)
* ですが、そのような認識では説明できない動作に困惑する時が来るかもしれません
* この記事では、私が実際に犯した勘違いを例に、deferの正しい動作を理解するためのポイントを解説します

# 前提：deferとは？
前回、deferの基本的な特徴をまとめた記事を書きました。一言で言うと、**関数の遅延実行の仕組み**です。詳しくはこちらをご覧ください。

https://qiita.com/kogamochiduki/items/abc8548fad2e8e382508

# 私の勘違い
Goのdeferを初めて触れたとき、私はこう思っていました。
#### deferは関数の終了時に実行される。つまり、関数を抜けた後に一気に処理がされるんだな!!
しかし、この認識は完全に正確ではありません。特にdeferの引数が関数呼び出しを含む場合、この認識だと間違った結果になります。私がその間違いに気づいたのが次のソースコードとなります。

# 本題：出力結果を予測してみよう
では、私が引っかかったソースコードを見ていきましょう。出力順に①〜④の番号を振っています。これらの番号に対する`message`の出力値を予測してみてください。
```sample1.go
package main

import (
	"fmt"
)

// グローバル変数
var message = "original message"

func changeMessage(newMessage string) (resetFunc func()) {
	// 現在のメッセージを保存
	tmp := message
	// 新しいメッセージに変更
	message = newMessage
	// 元に戻す関数を返す
	return func() {
		message = tmp
	}
}

func main() {
	// ①
	fmt.Println("Before doSomething:", message)

	// main関数の処理
	doSomething()

	// ④
	fmt.Println("After doSomething:", message)
}

func doSomething() {
	// ②
	fmt.Println("Before defer:", message)

	defer changeMessage("deferred message")()

	// ③
	fmt.Println("After defer:", message)
}
```
.
.
.
.
.
## 勘違いしていた時の私の予測
私の頭の中では、deferが最後に実行されるものだと信じていました。`doSomething`の最後で`message`が`deferred message`に変わるんでしょ？と。その為、次のような出力結果を予測していました。
```.予測
Before doSomething: original message
Before defer: original message
After defer: original message
After doSomething: deferred message
```
しかし、実際の出力結果は次のようになります。

## 出力結果
```sample1.go出力結果
Before doSomething: original message
Before defer: original message
After defer: deferred message
After doSomething: original message
```
* 「え？`After defer`で`message`が`deferred message`になっている？？」
* 「あれ、`defer`って呼び出し元の関数(今回で言うと`doSomething`)の最後に実行されるんじゃないの？？」

はい、当時の私はパニックです。

# 解説：deferは一番外側の関数を遅延実行させる仕組み
どうしてこのような出力結果となるのか、それは「deferの引数は宣言時に評価される」という特性を理解しきれていなかった為です。
deferに渡される引数が関数の場合、その引数の関数も即時評価(実行)されます。つまり、**deferは一番外側の関数を遅延実行させる仕組み**ということとなります。

改めて、`sample.1.go`のdeferの記述を見てみましょう。
```go
defer changeMessage("deferred message")()
```
この文では内側の関数を`changeMessage("deferred message")`、外側の関数を`changeMessage戻り値の無名関数`と分解できます。
その為、動作としては次のような流れになります。
1. `changeMessage("deferred message")`が即時実行される
    * この時点で`message`の値は`deferred message`に変わります
2. `changeMessage戻り値の無名関数`が、deferで遅延実行される関数として登録される
3. `doSomething`の終了時、deferに登録された無名関数が実行され、`message`の値を`original message`に戻す

# 余談：括弧を省略した場合
余談になりますが、deferに渡す関数の実行部分、つまり末尾の括弧を除去すると、どうなるのでしょうか？試してみましょう。
```go
// 省略

func doSomething() {
	// ②
	fmt.Println("Before defer:", message)

	// 末尾の括弧を除去してみる
    defer changeMessage("deferred message")

	// ③
	fmt.Println("After defer:", message)
}
```
この場合、`changeMessage("deferred message")`自身がdeferで遅延実行される関数として登録されます。

```.実行結果
Before doSomething: original message
Before defer: original message
After defer: original message
After doSomething: deferred message
```

# まとめ
今回のポイントです。
* **deferは一番外側の関数を遅延実行させる仕組み**です
* deferは「関数の終わりに実行される」ものではありますが、引数は即時評価されます
* そして、deferに渡される引数が関数の場合、その引数の関数も即時評価(実行)されます

deferを使用する際は、引数の評価タイミングを理解しないと誤解を招くことがありますので注意しましょう！

# 参考
https://go.dev/doc/effective_go#defer

https://qiita.com/Ishidall/items/8dd663de5755a15e84f2