func main() {
err := outerFunc(1)
if err != nil {
fmt.Println("print err in main:", err)
}
fmt.Println("Done")
}
func outerFunc(param int) (err error) {
defer func(err *error) {
fmt.Println("test 1 print err in defer func:", (*err).Error())
}(&err)
defer func(err error) {
fmt.Println("test 2 print err in defer func:", err)
}(err)
fmt.Println("Do in outerFunc")
if param > 0 {
err = fmt.Errorf("error param:%d", param)
return err
}
return nil
}
Do no thing in outerFunc
test 2 print err in defer func: <nil>
test 1 print err in defer func: error param:1
print err in main: error param:1
Done
原因分析
- golang函数只有值传递, 要实现函数内修改值, 只能传递指针(相当于地址值的值传递)
- defer只是执行顺序在函数的最后, 但是defer中的值还是根据执行顺序确定
- 也就是说, 在函数开头的defer只有命名参数返回值中的"err error", 把err作为nil指针处理,
- 但是穿的是地址值就不同, err指向始终不变, 只要执行*err即可获取err的最新内容,
- 不只是error处理, 任何需要在defer中处理的参数都应该考虑在defer中的值, 不放在函数头部, 而放在对参数的所有赋值操作都已经完成之后.
同理,我们在设计熔断器的函数时,最好是接收*error类型,因为你不知道调用者是不是用于defer时调用
onBreaker(breaker breaker.Breaker, err *error) {
if err != nil && *err != nil {
breaker.MarkFailed()
} else {
breaker.MarkSuccess()
}
}