设为首页 - 加入收藏 焦作站长网 (http://www.0391zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: 2019 广告 发布 全球
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

一文悟透备受争议的 Go 语言错误处理

发布时间:2019-09-21 01:22 所属栏目:[优化] 来源:佚名
导读:写过 C 的同学知道,C 语言中常常返回整数错误码(errno)来表示函数处理出错,通常用 -1 来表示错误,用 0 表示正确。 而在 Go 中,我们使用 error 类型来表示错误,不过它不再是一个整数类型,是一个接口类型: typeerrorinterface{ Error()string } 它表

写过 C 的同学知道,C 语言中常常返回整数错误码(errno)来表示函数处理出错,通常用 -1 来表示错误,用 0 表示正确。

一文悟透备受争议的 Go 语言错误处理

而在 Go 中,我们使用 error 类型来表示错误,不过它不再是一个整数类型,是一个接口类型:

  1. type?error?interface?{?
  2. ?Error()?string?
  3. }?

它表示那些能用一个字符串就能说清的错误。

我们最常用的就是 errors.New() 函数,非常简单:

  1. //?src/errors/errors.go?
  2. func?New(text?string)?error?{?
  3. ????return?&errorString{text}?
  4. }?
  5. type?errorString?struct?{?
  6. ????s?string?
  7. }?
  8. func?(e?*errorString)?Error()?string?{?
  9. ????return?e.s?
  10. }?

使用 New 函数创建出来的 error 类型实际上是 errors 包里未导出的 errorString 类型,它包含唯一的一个字段 s,并且实现了唯一的方法:Error() string。

通常这就够了,它能反映当时“出错了”,但是有些时候我们需要更加具体的信息,例如:

  1. func?Sqrt(f?float64)?(float64,?error)?{?
  2. ?if?f?
  3. ?return?0,?errors.New("math:?square?root?of?negative?number")?
  4. ?}?
  5. ?//?implementation?
  6. }?

当调用者发现出错的时候,只知道传入了一个负数进来,并不清楚到底传的是什么值。在 Go 里:

  1. It?is?the?error?implementation’s?responsibility?to?summarize?the?context.?

它要求返回这个错误的函数要给出具体的“上下文”信息,也就是说,在 Sqrt 函数里,要给出这个负数到底是什么。

所以,如果发现 f 小于 0,应该这样返回错误:

  1. if?f?
  2. ?return?0,?fmt.Errorf("math:?square?root?of?negative?number?%g",?f)?
  3. }?

这就用到了 fmt.Errorf 函数,它先将字符串格式化,再调用 errors.New 函数来创建错误。

当我们想知道错误类型,并且打印错误的时候,直接打印 error:

  1. fmt.Println(err)?

或者:

  1. fmt.Println(err.Error)?

fmt 包会自动调用 err.Error() 函数来打印字符串。

通常,我们将 error 放到函数返回值的最后一个,没什么好说的,大家都这样做,约定俗成。

参考资料【Tony Bai】这篇文章提到,构造 error 的时候,要求传入的字符串首字母小写,结尾不带标点符号,这是因为我们经常会这样使用返回的 error:

  1. ...?err?:=?errors.New("error?example")?
  2. fmt.Printf("The?returned?error?is?%s.\n",?err)?

error 的困局

  1. In?Go,?error?handling?is?important.?The?language’s?design?and?conventions?encourage?you?to?explicitly?check?for?errors?where?they?occur?(as?distinct?from?the?convention?in?other?languages?of?throwing?exceptions?and?sometimes?catching?them).?

在 Go 语言中,错误处理是非常重要的。它从语言层面要求我们需要明确地处理遇到的错误。而不是像其他语言,类如 Java,使用 try-catch- finally 这种“把戏”。

这就造成代码里 “error” 满天飞,显得非常冗长拖沓。

而为了代码健壮性考虑,对于函数返回的每一个错误,我们都不能忽略它。因为出错的同时,很可能会返回一个 nil 类型的对象。如果不对错误进行判断,那下一行对 nil 对象的操作百分之百会引发一个 panic。

这样,Go 语言中诟病最多的就是它的错误处理方式似乎回到了上古 C 语言时代。

  1. rr?:=?doStuff1()?
  2. if?err?!=?nil?{?
  3. ?//handle?error...?
  4. }?
  5. err?=?doStuff2()?
  6. if?err?!=?nil?{?
  7. ?//handle?error...?
  8. }?
  9. err?=?doStuff3()?
  10. if?err?!=?nil?{?
  11. ?//handle?error...?
  12. }?

Go authors 之一的 Russ Cox 对于这种观点进行过驳斥:当初选择返回值这种错误处理机制而不是 try-catch,主要是考虑前者适用于大型软件,后者更适合小程序。

在参考资料【Go FAQ】里也提到,try-catch 会让代码变得非常混乱,程序员会倾向将一些常见的错误,例如,failing to open a file,也抛到异常里,这会让错误处理更加冗长繁琐且易出错。

而 Go 语言的多返回值使得返回错误异常简单。对于真正的异常,Go 提供 panic-recover 机制,也使得代码看起来非常简洁。

当然 Russ Cox 也承认 Go 的错误处理机制对于开发人员的确有一定的心智负担。

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章