概述
延时处理,在 defer 归属的函数即将返回时,将延时处理的语句按照 defer 定义的逆序进行执行,先被 defer 的语句最后被执行。
拦截 panic,使程序回复正常运行;
优雅的关闭资源;
修改函数具名返回值;
输出调试信息;
还原旧变量的值;
执行时机
go 中的 return 不是原子操作,分为给返回值赋值和 ret 两步,而 defer 语句的执行就在返回值赋值操作后,ret 指令执行之前。
func main() {
fmt.Println(test())
}
func test() (res int) {
res = 1
defer func() {
fmt.Println("start", res)
res++
fmt.Println("end", res)
}()
return 7
}
//start 7
//end 8
//8
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
}
// defer注册要延迟执行的函数时该函数所有的参数都需要确定其值
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering :", s)
return s
}
func un(s string) {
fmt.Println("leaving :", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
//entering : b
//in b
//entering : a
//in a
//leaving : a
//leaving : b
使用时的问题
何时使用
append、cap、len、make、new 等内置函数是不可以直接作为 deferred 函数的,而 close、copy、delete、print、recover 等可以。注意 append 和 copy。
这些不可直接使用的可以使用一个包裹他的匿名函数来实现。
defer 关键字的求值时机
defer 关键字后面的表达式是在将 deferred 函数注册到 deferred 函数栈的时候进行求值的。
package main
import "fmt"
func foo1() {
for i := 0; i < 4; i++ {
defer fmt.Println(i)
}
}
func foo2() {
for i := 0; i < 4; i++ {
defer func(n int) {
fmt.Println(n)
}(i)
}
}
func foo3() {
for i := 0; i < 4; i++ {
defer func() {
fmt.Println(i)
}()
}
}
func main() {
fmt.Println("foo1: ")
foo1()
fmt.Println("foo2: ")
foo2()
fmt.Println("foo3: ")
foo3()
}
//foo1:
//3
//2
//1
//0
//foo2:
//3
//2
//1
//0
//foo3:
//4
//4
//4
//4
package main
import "fmt"
func foo1() {
sl := []int{1, 2, 3}
defer func(a []int) {
fmt.Println(a)
}(sl)
sl = []int{3, 2, 1}
_ = sl
}
func foo2() {
sl := []int{1, 2, 3}
defer func(p *[]int) {
fmt.Println(*p)
}(&sl)
sl = []int{3, 2, 1}
_ = sl
}
func main() {
foo1()
foo2()
}
//[1 2 3]
//[3 2 1]