go defer 坑

111 阅读1分钟

defer + recover + panic使用

一般我们都会用defer捕获panic,但是defer+recover的语法糖是有其需要注意的地方的:

func main() {
   funcA()
   funcB()
}

func funcA() {
   defer func() {
      if r := recover(); r != nil {
         fmt.Println("func A recover...")
      }
   }()

   panic("panic a test...")
}

func funcB() {
   defer func() {
      funcRecover()
   }()
   panic("panic b test...")
}

func funcRecover() {
   if r := recover(); r != nil {
      fmt.Println("funcRecover ....") // 并没有打印出来,即没有成功捕获panic
   }
}

代码运行结果是:

9bfba218-9401-4853-a8a0-deed694e30ce.jpeg

funcA的recover函数成功捕获到了panic。

funcB没有捕获到panic,发生了进程退出。

这是因为Golang语言里对应panic()+defer()+recover()这个语法糖组合起来会有一个潜在的坑:panic的错误只能在当前defer()中调用recover()捕获到,如果有嵌套方法,则recover无法捕获到panic错误,官方对应文档: go.dev/blog/defer-…

image.png

defer 传参

func main(){
    ctx := context.Background()
    var err error
    defer normalExec(ctx, err) // 直接调用函数

    err = fmt.Errorf("test err")
}

func normalExec(ctx context.Context, err error) {
   fmt.Printf("err is %v", err)
}

运行结果是:

image.png

func main(){
    ctx := context.Background()
    var err error
    defer func() {
        normalExec(ctx, err)  // 在匿名函数里调用
    }

    err = fmt.Errorf("test err")
}

func normalExec(ctx context.Context, err error) {
   fmt.Printf("err is %v", err)
}

image.png

可以看到,在defer函数里是否用匿名函数对结果的赋值有很大的影响。 假如不使用匿名函数,而是在defer里直接调用函数,即 defer normalExec(...),会导致函数参数先求值,从而导致err恒为nil