文章首发于:clawhub.club


问题代码:

1
2
3
4
5
6
7
8
9
10
11
12
func Test() error {
var err error
defer func() {
//恢复程序的控制权
iErr := recover()
if iErr != nil {
err = errors.New("err")
}
}()
panic("error")
return err
}

发生panic时,变量err赋值不生效。
解决后代码:

1
2
3
4
5
6
7
8
9
10
11
func Test() (err error) {
defer func() {
//恢复程序的控制权
iErr := recover()
if iErr != nil {
err = errors.New("err")
}
}()
panic("error")
return err
}

原因:
defer在匿名返回值和命名返回值函数中的不同,defer语句在方法返回“时”触发,也就是说return和defer是“同时”执行的。
执行过程:

  • 将result赋值给返回值(可以理解成Go自动创建了一个返回值retValue,相当于执行retValue = result)
  • 然后检查是否有defer,如果有则执行
  • 返回刚才创建的返回值(retValue)
    在这种情况下,defer中的修改是对result执行的,而不是retValue,所以defer返回的依然是retValue。
    在命名返回值方法中,由于返回值在方法定义时已经被定义,
    所以没有创建retValue的过程,result就是retValue,defer对于result的修改也会被直接返回。

参考:
Go语言中defer的一些坑