golang Error的一些坑

golang Error的一些坑

golang error的设计可能是被人吐槽最多的golang设计了。

最经典的err!=nil只影响代码风格设计,而有一些坑会导致我们的程序发生一些与我们预期不符的问题,开发过程中需要注意。

errors.Is​判断error是否Wrap不符合预期

errors.Is​经常用于判断当前的的error(第一个参数)是否包含target error(第二个参数),用于替代==​判断当前err与target err的关系,like:

golang 复制代码
const badInput = "abc"

var ErrBadInput = errors.New("bad input")

func validateInput(input string) error {
    if input == badInput {
        return fmt.Errorf("validateInput: %w", ErrBadInput)
    }
    return nil
}

func main() {
    input := badInput

    err := validateInput(input)
    if errors.Is(err, ErrBadInput) {
        fmt.Println("bad input error")
    }
}

结果输出:bad input error​ ,符合预期。

但是我们再来看看下面代码:

golang 复制代码
func main() {
	errMsg := "123"
	fmt.Println(errors.Is(errors.New(errMsg), errors.New(errMsg))) //结果反直觉,结果为false

	err123 := errors.New(errMsg)
	fmt.Println(errors.Is(err123, err123)) //结果为true
}

我们使用相同的errMsg初始化了两个err,使用errors.Is​比较,结果为false。

原因是errors.Is​虽然可以不断的拆包unwarp,但是在不能继续unwarp的时候,会用err == Error 直接比较错误对象与指定的错误对象,也就是指针的对比,肯定就对比不通过了。

这样很容易踩坑。

虽然erros.Is是会自动帮unwarp的,但是底层本质上还是指针的对比,new出来的err肯定是不同的err。

经验教训:

  • 不要使用erros.New的方式来判断error是否相等或包含,如果非要使用就用error()把error信息打出来来比较吧。

  • 为什么常能看见库设计者是直接实例化了一个public​的error对象来提供给你使用,而不是将其设置为private让你自己去new一个来判断(设计成private之后使用者就必须使用errors.Err()打印出msg来判断是否相等了,实在太蠢),like:

如果不了解erros.Is的使用可以参考:gosamples.dev/check-error...

自定义error判断是否为nil不符合预期

见代码:

golang 复制代码
type CustomizedError struct {
}

func (c CustomizedError) Error() string {
	return "CustomizedError"
}

func func1() error {
	var err *CustomizedError = nil
	return err
}

func main() {
	err := func1()
	if err != nil {
		log.Fatalf("1 err: %v", err) // 不符合预期,以为 err 为nil,但是会进这个分支
	}
}

上面判断不符合预期的原因是error类型是一个接口Interface,interface 设计为了两部分:

  • type
  • value

其中,value 由一个任意的具体值表示,称作 interface 的 dynamic value ;而 type 则对应该 value 的类型(即 dynamic type);例如对于对于 var a int = 3来说,把a 赋值给interface时, interface是使用(int, 3)进行存储的。

因此当想判断 interface 的值为 nil​时 ,则必须是其内部 value 和 type 均未设置的情况,即 (nil, nil)​ ;

在上面的代码案例中func1()​中返回值err的type已经不是nil了,因此后续会判断不通过。

禁言教训:

  • 自定义error的时候禁止在任何地方出现dynamic value为nil的error,like:var err *CustomizedError = nil,要么直接返回nil,要么初始化一个不为nil的返回。
  • 延续上一条:如果自己是库的提供者,因此避免暴露自定义的error类型,而是只提供 NewMyError() error 函数,避免使用方产生误用。like:
golang 复制代码
type myErr struct { // 注意这里的设计,myErr没有暴露出去
   code int
   msg  string
}

func (e myErr) Error() string {   
   return fmt.Sprintf("code:%d,msg:%v", e.code, e.msg)
}

func New(code int, msg string) error {// 注意这里的设计,myErr没有暴露出去,只提供一个返回error的初始化方法
   return myErr{
      code: code,
      msg:  msg,
   }
}

func GetCode(err error) int {
   if e, ok := err.(myErr); ok {
      return e.code
   }
   return -1
}

func GetMsg(err error) string {
   if e, ok := err.(myErr); ok {
      return e.msg
   }
   return ""
}

参考资料:

coolshell.cn/articles/21...

juejin.cn/post/697403...

感谢大家阅读到这里!🎉 如果你有任何问题或想法,欢迎在评论区留言,我们一起讨论、一起进步! 如果你觉得这篇文章对你有所帮助,也请不吝点赞、分享,支持博主继续创作更多优质内容!💪 关注【小菜先生的编程随想】公众号,我们一起在编程的道路上越走越远,战胜一切挑战!💥 希望可以下次再见!👋

相关推荐
ai小鬼头6 小时前
Ollama+OpenWeb最新版0.42+0.3.35一键安装教程,轻松搞定AI模型部署
后端·架构·github
萧曵 丶6 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
老任与码7 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
华子w9089258597 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
星辰离彬8 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
GetcharZp9 小时前
彻底告别数据焦虑!这款开源神器 RustDesk,让你自建一个比向日葵、ToDesk 更安全的远程桌面
后端·rust
jack_yin10 小时前
Telegram DeepSeek Bot 管理平台 发布啦!
后端
小码编匠10 小时前
C# 上位机开发怎么学?给自动化工程师的建议
后端·c#·.net
库森学长10 小时前
面试官:发生OOM后,JVM还能运行吗?
jvm·后端·面试
转转技术团队10 小时前
二奢仓店的静默打印代理实现
java·后端