当前位置: 首页 > news >正文

golang Error的一些坑

golang Error的一些坑

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

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

image

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

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

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​ ,符合预期。

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

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:

image

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

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

见代码:

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:

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 ""
}

参考资料:

https://coolshell.cn/articles/21140.html

https://juejin.cn/post/6974037920567017509

相关文章:

  • 解决Centos使用yum命令报错“Cannot find a valid baseurl for repo: base/7/x86_64”问题
  • c++(红黑树以及封装)
  • tradingview 2022版和2024版本的jsapi对接。
  • Java定时任务的三重境界:从单机心跳到分布式协调
  • UNIX网络编程笔记:基本TCP套接字编程
  • CSS平面转换
  • 万用表测MOS好坏
  • Java EE(13)——网络编程——UDP/TCP回显服务器
  • 本地生活服务APP开发,市场发展全新商业机遇
  • 【day1】数据结构刷题 链表
  • 运算符重载(关键字operator的使用)
  • 2025年3月AI搜索发展动态与趋势分析:从技术革新到生态重构
  • CUDA 学习(3)——CUDA 初步实践
  • 【Spring】Spring Task详解
  • DeepSeek-V3到DeepSeek-R1的演进
  • 如何在Visual Studio和 .NET 7中使用C#配置代理服务器进行网页抓取,并使用HtmlAgilityPack进行HTML解析
  • React学习笔记20
  • 【分布式】冰山(Iceberg)与哈迪(Hudi)对比的基准测试
  • 开发语言漫谈-groovy
  • 二分查找------练习1
  • 以军向也门3个港口的居民发布撤离令
  • MSCI中国指数5月调整:新增5只A股、1只港股
  • 中巴续签双边本币互换协议,进一步深化金融战略合作
  • 安徽省委副秘书长、省委政研室主任余三元调任省社科院院长
  • “水运江苏”“航运浙江”,江浙两省为何都在发力内河航运?
  • 2025上海科技节本周六启幕,机器人和科学家同走AI科学红毯