前言
三个奇特的关键字defer
/recover
/Panic
defer
defer
用于当前函数作用域结束后,执行 defer 后定义的代码。也就是说 defer 可以推迟语句的执行。
规则
- 一个函数体内可以包含多个 defer,多个 defer 会暂存在队列中,并且多个 defer 的执行顺序是逆序的,即先进后出。
- defer 进暂存队列之前,其推迟的代码中与主函数同作用域下的变量若已赋值,则变量需带值进队列;
package main
import "fmt"
func main() {
for i := 1; i <= 4; i++ {
defer fmt.Println("deferred", -i)
fmt.Println("regular", i)
}
}
#输出
regular 1
regular 2
regular 3
regular 4
deferred -4
deferred -3
deferred -2
deferred -1
推迟函数
defer funcA() // 推迟一个已有的函数funcA
defer func(<args>){}(<vars>) // 推迟一个全新的函数,vars是传入变量,args是形参。
args和vars是可选的。但 defer func(){ xxx }()
格式不可变。
特殊例子
- 情况1:
defer
中的变量与函数返回值变量一致,导致返回结果超出预期
func test01()(p int){
defer func(){
p++
}()
return 0
}
func main(){
f := test01()
fmt.Printf("f是:%d\n",f)
}
#输出
f是:1
上述例子中,
- defer 推迟 p++
- 函数 test01 执行完毕后,return 0,传递给返回变量p;
- 执行 p++
- p = 1
- 情况2:
defer
与当前函数构成闭包。
package main
import "fmt"
func test01(x int)(p int){
defer func(y int){
y++
p=x+y
}(x)
x++
return 0
}
func main(){
f := test01(2)
fmt.Println(f)
}
上述例子中,:
- 执行test01(2);
- 推迟 func(y int) { y++; p=x+y} (2)
- test01(2) 执行完,return 0,传递给返回变量 p。此时 p = 0,x = 3;
- 因 defer 调用了 x,所以 test01 虽然执行完,但 x 无法释放。
- 执行 func(y int) { y++; p=3+y} (2) 得 y=3;p=3+y
- p = 6
常用方式
defer
函数的一个典型用例是在使用完文件后将其关闭.
package main
import (
"io"
"os"
"fmt"
)
func main() {
newfile, error := os.Create("learnGo.txt")
if error != nil {
fmt.Println("Error: Could not create file.")
return
}
defer newfile.Close()
if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
fmt.Println("Error: Could not write to file.")
return
}
newfile.Sync()
}
panic
调用内置 panic()
函数可以停止 Go 程序中的正常控制流,但不会影响到panic()
调用前由defer
扔到堆栈的延迟函数代码。