Deferred вызовы складываются в стек (LIFO — last in, first out):
func example() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("main")
}
// main
// 3
// 2
// 1
Аргументы defer вычисляются сразу (в момент defer), а не при выполнении:
x := 10
defer fmt.Println(x) // напечатает 10, не 20
x = 20
Но если defer вызывает метод на указателе или замыкание — оно увидит актуальное значение:
x := 10
defer func() { fmt.Println(x) }() // напечатает 20
x = 20
Типичное использование — cleanup в обратном порядке:
f, _ := os.Open("file")
defer f.Close()
mu.Lock()
defer mu.Unlock()