Golang的异常处理机制
Whatever you do, always check your errors!
首先,不管在任何的编程语言,Error Check本身是一件重要的事情,是不能忽略的。
我们讨论的是如何优雅地进行Error Checking,而不是略过这一步
It’s worth stressing that whatever the design, it’s critical that the program check the errors however they are exposed. The discussion here is not about how to avoid checking errors, it’s about using the language to handle errors with grace.
Error Check有两种方式
第一种是 马上进行处理。它的好处是能够做到更加fine-grained的错误处理,能够知道具体处理到了哪一步。缺点是 正常的control flow和 异常处理flow 的代码交织在一起,代码逻辑看起来不是很清晰。
第二种是 等所有的操作完成了之后,再统一进行处理。这种比较类似 Java的Exception处理机制 Try-Catch-Finally. 这样子的优点是正常处理逻辑和异常处理逻辑解耦合,这样子control flow看起来会更加清晰。缺点是 无法知道 现在程序处理到了具体哪一部分出错的。
Go的Error Check设计理念
Golang没有内置Exception的语法,这种设计隐性在鼓励人们把一些诸如打开文件失败等常见的错误,当成是异常。同时使得语法更加简洁。
Go采用了一种不一样的设计。对于简单的错误处理,通过Go的多值返回特性能够让报告一个错误变得简单,而且这样不会重载返回值。(PS: 在Java中通过throws exception来实现报告错误,Go是通过直接return error来报告错误)。Golang的Error其实是value,我们可以基于value来实现上面任意一种Error Check方式。
考虑下面的两种代码呈现方式:
_, err = fd.Write(p0[a:b])
if err != nil {
return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
return err
}
// and so on
// 以下实现了一个error writer,用于滞后error checking处理
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) write(buf []byte) {
if ew.err != nil {
return
}
_, ew.err = ew.w.Write(buf)
}
// 经过改造之后的代码,看起来control flow会更加简洁
ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
return ew.err
}
Go的Panic机制
错误返回值,是一个值。
而panic这种,才是真正的异常,一般报panic都是库设计者认为这种情况不可能出现的时候才会使用。
Go同样有一些内置的方法来从真正那些异常的情况中唤起或者恢复程序。恢复机制(recovery mechanism)只有在程序状态为崩溃的时候才会执行,这种机制足够应付程序的崩溃,而且不需要使用上述那种控制结构便能做到,这样代码也会很整洁。
参考资料