设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 重新 试卷 创业者
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

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

发布时间:2019-09-19 12:45 所属栏目:21 来源:佚名
导读:譬如下面这段伪代码: funcfn()error{ x,err:=bar.Foo() iferr!=nil{ returnerr } //usex returnnil } 作为调用者,调用完 Foo 函数后,只用知道 Foo 是正常工作还是出了问题。也就是说你只需要判断 err 是否为空,

譬如下面这段伪代码:

  1. func fn() error { 
  2.     x, err := bar.Foo() 
  3.     if err != nil { 
  4.         return err 
  5.     } 
  6.      
  7.     // use x 
  8.     return nil 

作为调用者,调用完 Foo 函数后,只用知道 Foo 是正常工作还是出了问题。也就是说你只需要判断 err 是否为空,如果不为空,就直接返回错误。否则,继续后面的正常流程,不需要知道 err 到底是什么。

这就是处理 Opaque errors 这种类型错误的策略。

当然,在某些情况下,这样做并不够用。例如,在一个网络请求中,需要调用者判断返回的错误类型,以此来决定是否重试。这种情况下,作者给出了一种方法:

  1. type temporary interface { 
  2.     Temporary() bool 
  3. func IsTemporary(err error) bool { 
  4.     te, ok := err.(temporary) 
  5.     return ok && te.Temporary() 

就是说,不去判断错误的类型到底是什么,而是去判断错误是否具有某种行为,或者说实现了某个接口。

来个例子:

type temporary interface { Temporary() bool}func IsTemporary(err error) bool { te, ok := err.(temporary) return ok && te.Temporary()}

拿到网络请求返回的 error 后,调用 IsTemporary 函数,如果返回 true,那就重试。

这么做的好处是在进行网络请求的包里,不需要 import 引用定义错误的包。

handle not just check errors

这一节要说第二句箴言:“Don’t just check errors, handle them gracefully”。

  1. func AuthenticateRequest(r *Request) error { 
  2.  err := authenticate(r.User) 
  3.  if err != nil { 
  4.  return err 
  5.  } 
  6.  return nil 

上面这个例子中的代码是有问题的,直接优化成一句就可以了:

  1. func AuthenticateRequest(r *Request) error { 
  2.  return authenticate(r.User) 

还有其他的问题,在函数调用链的最顶层,我们得到的错误可能是:No such file or directory。

这个错误反馈的信息太少了,不知道文件名、路径、行号等等。

尝试改进一下,增加一点上下文:

  1. func AuthenticateRequest(r *Request) error { 
  2.  err := authenticate(r.User) 
  3.  if err != nil { 
  4.  return fmt.Errorf("authenticate failed: %v", err) 
  5.  } 
  6.  return nil 

这种做法实际上是先错误转换成字符串,再拼接另一个字符串,最后,再通过 fmt.Errorf 转换成错误。这样做破坏了相等性检测,即我们无法判断错误是否是一种预先定义好的错误了。

应对方案是使用第三方库:github.com/pkg/errors。提供了友好的界面:

  1. // Wrap annotates cause with a message. 
  2. func Wrap(cause error, message string) error 
  3. // Cause unwraps an annotated error. 
  4. func Cause(err error) error 

通过 Wrap 可以将一个错误,加上一个字符串,“包装”成一个新的错误;通过 Cause 则可以进行相反的操作,将里层的错误还原。

有了这两个函数,就方便很多:

  1. func ReadFile(path string) ([]byte, error) { 
  2.     f, err := os.Open(path) 
  3.     if err != nil { 
  4.         return nil, errors.Wrap(err, "open failed") 
  5.     } 
  6.     defer f.Close() 
  7.      
  8.     buf, err := ioutil.ReadAll(f) 
  9.     if err != nil { 
  10.         return nil, errors.Wrap(err, "read failed") 
  11.     } 
  12.     return buf, nil 

这是一个读文件的函数,先尝试打开文件,如果出错,则返回一个附加上了 “open failed” 的错误信息;之后,尝试读文件,如果出错,则返回一个附加上了 “read failed” 的错误。

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读