什么是错误处理
异常处理是任何语言都不能绕不开的话题。Go 语言没有提供传统的 try...catch
语句来处理异常,而是通过使用 error
来处理错误,用 panic
和 recover
来处理异常。
错误封装是将一个错误包裹到另一个错误的过程。假设我们有一个访问数据库的 Web 服务器,并试图从数据库中获取一条记录。如果数据库调用返回一个错误,我们可以决定是捕获这个错误还是从网络服务中发送我们自己的自定义错误。
基础 error
error 接口
error 是一种内建的接口类型,内建意味着不需要 import
任何包就可以直接使用,使用起来就像基础类型一样自然。
type error interface { Error() string }
error 接口只声明了一个 Error()
方法,任何实现了该方法的结构体都可以作为 error 来使用。
error
的实例代表一种异常状态Error()
方法用于描述该异常状态- 值为
nil
的 error 代表没有异常
标准库 errors
包中的 errorString
就是实现 error 接口的一个例子:
type errorString struct { s string } func (e *errorString) Error() string { return.s }
创建 error
标准库提供了两种 error 的方法:
errors.New()
fmt.Errorf()
error.New()
的实现极其简单,只是简单地构造一个errorString
实例便返回:
package errors func New(text string) error { return &errorString{text} }
fmt.Errorf()
error.New()
单调地接收一个字符串参数来构造 error,而实际场景中往往需要使用 fmt.Sprintf()
生成字符串,这时可以直接使用 fmt.Errorf()
:
package fmt func Errorf(format string, a ...interface{}) error { return errors.New(Sprintf(format, a...))
联系与区别: fmt.Errorf()
只是对 error.New()
的简单封装,使用前者可以使得代码更加简洁。而两者的区别在于 fmt.Errorf()
适用于需要格式化输出错误字符串的场景,如果不需要格式化字符串,则建议使用 error.New()
。
异常处理
针对 error 而言,异常处理包括如何检查错误,如何传递错误。
- 检查 error
最常见的检查 error
的方式是与 nil
值进行比较:
if err != nil { // 错误处理逻辑 }
与预定义的 error 进行比较(可以是自定义也可以是标准库中的,比如 os
库中的常见错误):
var noRows = errors.New("no rows found") if err == noRows { }
- 传递 error
在一个函数中收到一个 error,往往需要附加一些上下文信息再把 error 继续向上层抛。
可以看一下如下的例子:
package main import ( "errors" "fmt" ) var noRows = errors.New("no rows found") func getRecords() error { return noRows } func webService() error { if err := getRecords(); err != nil { return fmt.Errorf("Error %s when calling DB", err) } return nil } func main() { if err := webService(); err != nil { fmt.Printf("Error: %s when calling webService\n", err) return } fmt.Println("webService call successful") }
运行结果:
$ go run . Error: Error no rows found when calling DB when calling webService
在上面的程序中,我们在调用 getRecords
函数时,发送错误的字符串描述,虽然这可能看起来像错误处理,但实际上并不是。让我们接着看下去吧。