Go语言的有效错误处理

开发 前端
中午闲暇翻看Daniel Morsing的“The Go scheduler”时,发现其另外一篇短文“Effective error handling in Go”,文章不长,但感觉对Go中错误处理方法总结的还是比较到位的,这里译之供大家参考。

中午闲暇翻看Daniel Morsing的“The Go scheduler”时,发现其另外一篇短文“Effective error handling in Go”,文章不长,但感觉对Go中错误处理方法总结的还是比较到位的,这里译之供大家参考。

一、简介

Go语言受到诟病最多的一项就是其错误处理机制。如果显式地检查和处理每个error,这恐怕的确会让人望而却步。你可以试试这里列出的几个方法,以避免你走入错误处理方法的误区当中去。

二、在缩进区处理错误

当使用Go语言编写代码时,***下面这样的错误处理方法:

  1. f, err := os.Open(path) 
  2. if err != nil { 
  3.     // handle error 
  4. // do stuff 
  5.  
  6. 而不是下面这样的: 
  7.  
  8. f, err := os.Open(path) 
  9. if err == nil { 
  10.     // do stuff 
  11. // handle error 

按照上面的方法处理错误,处理正常情况的代码读起来就显得通篇连贯了。

三、定义你自己的errors

做好如何正确进行错误处理的***步就是要了解error是什么。如果你设计实现的包会因某种原因发生某种错误,你的包用户将会对错误的原因很感兴趣。为了满足用户的需求,你需要实现error接口,简单做起来就像这样:

  1. type Error string 
  2. func (e Error) Error() string { return string(e) } 

现在,你的包用户通过执行一个type assertion就可以知道是否是你的包导致了这个错误:

  1. result, err := yourpackage.Foo() 
  2. if ype, ok := err.(yourpackage.Error); ok { 
  3.     // use ype to handle error 

通过这个方法,你还可以向你的包用户暴露更多地结构化错误信息:

  1. type ParseError struct { 
  2.     File  *File 
  3.     Error string 
  4.  
  5. func (oe *ParseError) Error() string {//译注:原文中这里是OpenError 
  6.     // format error string here 
  7.  
  8. func ParseFiles(files []*File) error { 
  9.     for _, f := range files { 
  10.         err := f.parse() 
  11.         if err != nil { 
  12.             return &ParseError{ //译注:原文中这里是OpenError 
  13.                 File:  f, 
  14.                 Error: err.Error(), 
  15.             } 
  16.         } 
  17.     } 

通过这种方法,你的用户就可以明确地知道到底哪个文件出现解析错误了。(译注:从这里看到的go语言error设计之内涵,让我想起了Rob Pike大神的一篇Blog:"少即是级数级的多")

不过包装error时要小心,当你将一个error包装起来后,你可能会丢失一些信息:

  1. var c net.Conn 
  2. f, err := DownloadFile(c, path) 
  3. switch e := err.(type) { 
  4. default
  5.     // this will get executed if err == nil 
  6. case net.Error: 
  7.     // close connection, not valid anymore 
  8.     c.Close() 
  9.     return e 
  10. case error: 
  11.     // if err is non-nil 
  12.     return err 
  13. // do other things. 

如果你包装了net.Error,上面这段代码将无法知道是由于网络问题导致的失败,会继续使用这条无效的链接。

有一条经验规则:如果你的包中使用了一个外部interface,那么不要对这个接口中方法返回的任何错误,使用你的包的用户可能更关心这些错误,而不是你包装后的错误。

四、将错误作为状态

有时,当遇到一个错误时,你可能会停下来等等。这或是因为你将延迟报告错误,又或是因为你知道如果这次报告后,后续你会再报告同样的错误。

***种情况的一个例子就是bufio包。当一个bufio.Reader遇到一个错误时,它将停下来保持这个状态,直到buffer已经被清空。只有在那时它才会报告错误。

第二种情况的一个例子是go/loader。当你通过某些参数调用它导致错误时,它会停下来保持这个状态,因为它知道你很可能会使用同样地参数再次调用它。

五、使用函数以避免重复代码

如果你有两段重复的错误处理代码,你可以将它们放到一个函数中去:

  1. func handleError(c net.Conn, err error) { 
  2.     // repeated error handling 
  3.  
  4. func DoStuff(c net.Conn) error { 
  5.     f, err := downloadFile(c, path) 
  6.     if err != nil { 
  7.         handleError(c, err) 
  8.         return err 
  9.     } 
  10.  
  11.     f, err := doOtherThing(c) 
  12.     if err != nil { 
  13.         handleError(c, err) 
  14.         return err 
  15.     } 

优化后的实现方法如下:

  1. func handleError(c net.Conn, err error) { 
  2.     if err == nil { 
  3.         return 
  4.     } 
  5.     // repeated error handling 
  6.  
  7. func DoStuff(c net.Conn) error { 
  8.     defer func() { handleError(c, err) }() 
  9.     f, err := downloadFile(c, path) 
  10.     if err != nil { 
  11.         return err 
  12.     } 
  13.  
  14.     f, err := doOtherThing(c) 
  15.     if err != nil { 
  16.         return err 
  17.     } 

这就是全部了。就Go语言错误处理而言,我知道的就这么多了。

责任编辑:张伟 来源: Tony Bai的博客
相关推荐

2021-04-29 09:02:44

语言Go 处理

2021-09-13 07:53:31

Go错误处理

2022-09-05 08:55:15

Go2提案语法

2017-09-22 15:25:40

Go语言其他语言错误处理

2023-10-26 15:49:53

Go日志

2021-09-27 15:33:48

Go 开发技术

2020-12-17 06:25:05

Gopanic 模式

2021-09-27 23:28:29

Go多协程并发

2021-09-27 10:04:03

Go程序处理

2023-03-10 08:48:29

2021-04-14 07:08:14

Nodejs错误处理

2022-06-26 23:03:14

Go标准库语言

2022-07-13 08:53:28

函数Go语言

2022-08-01 08:48:39

Go代码接口

2023-12-26 22:05:53

并发代码goroutines

2022-07-08 08:55:56

Go函数模型

2022-10-24 08:55:13

Go工具链开发者

2022-12-12 08:53:53

Go版本方式

2022-05-26 08:53:47

Go函数代码

2009-08-05 16:04:50

点赞
收藏

51CTO技术栈公众号