1.7 错误和异常
错误处理是每个编程语言都要考虑的一个重要话题。在Go语言的错误处理中,错误是软件包API和应用程序用户界面的一个重要组成部分。
在程序中总有一部分函数总是要求必须能够成功的运行。比如strconv.Itoa将整数转换为字符串,从数组或切片中读写元素,从map读取已经存在的元素等。这类操作在运行时几乎不会失败,除非程序中有BUG,或遇到灾难性的、不可预料的情况,比如运行时的内存溢出。如果真的遇到真正异常情况,我们只要简单终止程序就可以了。
排除异常的情况,如果程序运行失败仅被认为是几个预期的结果之一。对于那些将运行失败看作是预期结果的函数,它们会返回一个额外的返回值,通常是最后一个来传递错误信息。如果导致失败的原因只有一个,额外的返回值可以是一个布尔值,通常被命名为ok。比如,当从一个map查询一个结果时,可以通过额外的布尔值判断是否成功:
if v, ok := m["key"]; ok {
return v
}但是导致失败的原因通常不止一种,很多时候用户希望了解更多的错误信息。如果只是用简单的布尔类型的状态值将不能满足这个要求。在C语言中,默认采用一个整数类型的errno来表达错误,这样就可以根据需要定义多种错误类型。在Go语言中,syscall.Errno就是对应C语言中errno类型的错误。在syscall包中的接口,如果有返回错误的话,底层也是syscall.Errno错误类型。
比如我们通过syscall包的接口来修改文件的模式时,如果遇到错误我们可以通过将err强制断言为syscall.Errno错误类型来处理:
err := syscall.Chmod(":invalid path:", 0666)
if err != nil {
log.Fatal(err.(syscall.Errno))
}我们还可以进一步地通过类型查询或类型断言来获取底层真实的错误类型,这样就可以获取更详细的错误信息。不过一般情况下我们并不关心错误在底层的表达方式,我们只需要知道它是一个错误就可以了。当返回的错误值不是nil时,我们可以通过调用error接口类型的Error方法来获得字符串类型的错误信息。
在Go语言中,错误被认为是一种可以预期的结果;而异常则是一种非预期的结果,发生异常可能表示程序中存在BUG或发生了其它不可控的问题。Go语言推荐使用recover函数将内部异常转为错误处理,这使得用户可以真正的关心业务相关的错误处理。
如果某个接口简单地将所有普通的错误当做异常抛出,将会使错误信息杂乱且没有价值。就像在main函数中直接捕获全部一样,是没有意义的:
func main() {
defer func() {
if r := recover(); r != nil {
log.Fatal(r)
}
}()
...
}捕获异常不是最终的目的。如果异常不可预测,直接输出异常信息是最好的处理方式。
学员评价