原创文章,转载请注明出处:服务器非业余研究-sunface
最近看谷歌go group里面有很多讨论go error处理风格的问题,颇有启发,现在跟大家分享一下,首先请看一个提问:
Hi folks,
x, err := foo()if err != nil {return err}y, err := bar(x)if err != nil {return err}z, err := baz(y)if err != nil {return err}// do something w/ zreturn nil
var (x, y, z Terr error)x, err = foo()if err == nil {y, err = bar(x)}if err == nil {z, err = baz(y)}
if err == nil {
// do something w/ z
}
return err
//出于好奇,请问有人用过我这种风格然后有发现过缺点吗?或者这种风格并不好?虽然我认为很好
看了上面两种错误处理风格,大家应该已经看出了部分端倪:第一种是比较传统的error处理方式,第二种是作者认为比较创新的error处理方式。先不说第一种的优缺点,
第二种首先在开头设置了所有接收error的变量,然后后面的error处理方式是环环相扣,冗余度非常之高,且直到最后才return,也就是说如果代码够长,那你得看完中间所有的处理过程,在这里你只有读完了整段代码才知道foo()产生的错误到底是怎么处理的。
然后一个评论中提出了一种个人觉得不错的风格:
value,err := bar()
if err != nil {
goto handleError
}
.
.
.
.
handleError:
return err
在这种风格中不需要去知道goto和label中间的代码,可以直接进行错误处理并返回,因此代码简洁、可读性强、性能高且冗余度低。
还有一个评论提出了一种很特别的风格:
if x, err := foo(); err != nil {
return err
} else if y, err := bar(x); err != nil {
return err
} else if z, err := baz(y); err != nil {
return err
} else {
// do something w/ x,y,z
return nil
}
这种写法的代码比较简洁,return的地方一眼便可得知。
下面这种风格可以节省大量的if语句
func checkErr( err error) {
if err != nil {
//deal error here
}
}
func main() {
ting,err := whatever()
checkErr(err)
}
下面再说说我个人对提问中第二段代码的看法,主要有3点:
1.在第二段代码中,所有的有意义的代码都是环环相扣,每一个if和return都要记在脑袋里,具体可以看下这篇文章:代码的嵌套——各种状态的组合。在第一段代码中所有的错误处理都是在产生错误代码的下面,很容易发现什么代码导致了某个错误,而且在那些if代码块之后,你不需要担心之前的x,y,z,因为它们都是合法的。但是在第二段代码中,永远不能知道x,y,z是否合法,每次都得继续验证前面的值。
2.因为第二段代码的错误处理和返回方式(环环相扣),我们就无法得知error的具体位置,唯一能做的就是彻底放弃然后无奈的说一句“代码有BUG了”,仅此而已。
3.每次你往第二段代码的函数增加新的代码的时候,都要放在一个if代码快中。相反在第一段代码中,就不需要,因为只有错误处理在if中,其他的都在外部处理
这里只是一个简单的抛砖引玉,希望大家能发表下平常项目中使用的错误处理机制,一起讨论。