LoginSignup
6
0

Goのdefer挙動確認メモ

Last updated at Posted at 2023-09-06

初めてのGo言語を読んでdeferの挙動が気になったので、確認した結果のメモ。

実行順序

実行タイミング

func main() {
    fmt.Println("start")
    defer fmt.Println(1)
    fmt.Println("end")
}

出力

start
end
1

deferを除く処理が完了した後、defer実行。

defer文を複数書いた場合

func main() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
}

出力

3
2
1

宣言順とは逆順で実行。

関数がreturnする関数をdefer指定した場合

func main() {
    fmt.Println("main start")

    // returnする関数をdefer指定 (関数後ろが()())
    defer func() func() {
        fmt.Println("func1")
        return func() {
            fmt.Println("return1")
        }
    }()()

    // 外側?の関数をdefer指定 (関数後ろが())
    defer func() func() {
        fmt.Println("func2")
        return func() {
            fmt.Println("return2")
        }
    }()

    defer fmt.Println("noname func")
    fmt.Println("main end")
}

出力

main start
func1
main end
noname func
func2
return1

関数が返す関数をdefer指定(1つ目のdefer)した場合、外側?の関数のreturnまではmainの流れの通りに実行される。そこで返された関数の実行は、普通のdeferと同様の順序で行われる。
また、2つ目のdefer指定した関数が返す関数は実行されない。

戻り値への影響

名前付き戻り値を使わない場合

// returnした変数と同名の変数を変更
func func1() string {
	result := "func1"
	defer func() {
		fmt.Println("defer実行")
		result = "changed"
	}()
	return result
}

// defer指定した関数からstringをreturn
func func2() string {
	defer func() string {
		return "changed"
	}()
	return "func2"
}

func main() {
	fmt.Println(func1())
	fmt.Println(func2())
}

出力

defer実行
func1
func2

deferも実行はされるが、defer指定した関数が行ったresultの変更は戻り値に反映されない。
また、defer指定した関数からreturnしたとしても、戻り値は変わらない。

名前付き戻り値と違う変数名の変数をreturnした場合

func func1() (s string) {
    s = "start"
    result := "result"
    defer func() {
        fmt.Println(s)
        s = "changed"
    }()
    return result
}

func main() {
    fmt.Println(func1())
}

出力

result
changed

returnする変数名に関わらず、名前付き戻り値として指定した変数名で戻り値の参照・変更が可能。

変数の評価タイミング

defer指定した関数に引数として与えない場合

func main() {
    result := "start"
    defer func() {
        fmt.Println(result)
    }()
    result = "end"
}

出力

end

main関数終了時に変数を評価。

defer指定した関数に引数として与える場合

func main() {
	result := "start"
	defer func(result string) {
		fmt.Println(result)
	}(result)
	result = "end"
}

出力

start

defer通過時に変数を評価。

その他

os.Exit()で関数を終了した場合

func main() {
	defer fmt.Println(1)

	fmt.Println("end")
	os.Exit(0)
}

出力

end

deferは実行されない。

6
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0