go☞010特殊控制流(defer panic recover)

阅读量: zyh 2020-10-14 21:40:12
Categories: > Tags:

前言

三个奇特的关键字defer/recover/Panic

defer

defer用于当前函数作用域结束后,执行 defer 后定义的代码。也就是说 defer 可以推迟语句的执行。

规则

  1. 一个函数体内可以包含多个 defer,多个 defer 会暂存在队列中,并且多个 defer 的执行顺序是逆序的,即先进后出。
  2. 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 }() 格式不可变。

特殊例子

func test01()(p int){
    defer func(){
        p++
    }()
    return 0
}

func main(){
    f := test01()
    fmt.Printf("f是:%d\n",f)
}
#输出
f是:1

上述例子中,

  1. defer 推迟 p++
  2. 函数 test01 执行完毕后,return 0,传递给返回变量p;
  3. 执行 p++
  4. p = 1
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)
}

上述例子中,:

  1. 执行test01(2);
  2. 推迟 func(y int) { y++; p=x+y} (2)
  3. test01(2) 执行完,return 0,传递给返回变量 p。此时 p = 0,x = 3;
  4. 因 defer 调用了 x,所以 test01 虽然执行完,但 x 无法释放。
  5. 执行 func(y int) { y++; p=3+y} (2) 得 y=3;p=3+y
  6. 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扔到堆栈的延迟函数代码。