引言
go 包实现了处理 error 的一些功能。
这是源码注释
// Package errors implements functions to manipulate errors.
errors 包的源码放在 $GOROOT/src/errors
中
查看安装目录
go env GOROOT
为了方便阅读源码和调试,建议将源码复制后作为新项目打开
cp -r errors ~/
文件
errors 包有两个文件,errors.go 和 wrap.go 以及三个测试文件。
errors.go
errors.go 中有一个 errorString 结构体
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
读注释我们明白,这个结构体是 error 的简单实现。我们可以自定义错误的类型,而这个结构体就是源码中对错误的一个简单实现。
然后有这个结构体的 constructor,即 New 函数
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
我们发现返回一个 error, 继续读源码,我们知道这是一个接口,在 builtin 包 builtin.go 文件中, builtin 包用来给有预定义标识符生成文档,也就是说这个包里都是一些预定义标识符,这些标识符的具体实现没有在这个包里,写这个包只是为了使用 goget 生成文档。
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
这个接口是用来表示错误状态的,只要实现了 Error() 方法,那么就实现了这个接口。也就是说,我们可以自定义一个类型,然后实现 Error() 方法,这个类型就是我们自定义的错误类型。当这个接口为 nil 时,没有错误,所以我们一般来判断这个接口是否为空,来判断是否出错。
func main() {
if err := A(); err != nil {
//
}
}
余下的 Error() 函数就是 errorString 的方法,所以 errorString 实现了 error 接口,所以 errorStirng 可以转换为 error 类型,所以才返回 errorString 类型,而函数签名处是 error 类型。
我们可以看 fmt 包 errors.go 文件中,就自定义了一个错误类型
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
从上面的代码我们可以知道,errors.go 文件主要就是 围绕 error 接口展开的,接下来我们来看测试文件中具体的例子。
example_test.go
自定义一个类型
// MyError is an error implementation that includes a time and message.
type MyError struct {
When time.Time
What string
}
然后实现 error 接口
func (e MyError) Error() string {
return fmt.Sprintf("%v: %v", e.When, e.What)
}
这里使用了 opps 函数封装了一下,我们可以模仿 errors.go 中把它改为 constructor,即 New 函数。
func New(s string) error {
return &MyError{time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC), s}
}
func main() {
if err := New("haha"); err != nil {
fmt.Println(err)
}
}
可得 1989-03-15 22:30:00 +0000 UTC: haha
,这个即我们定义的 error 类型的具体实现。
errors_test.go
- ExampleNew()
有测试 New 函数的,即新生成一个 error 类型,这个类型的格式只有一个 string.
func ExampleNew() {
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
// Output: emit macho dwarf: elf header corrupted
}
- TestErrorMethod()
有测试方法的,我们生成一个 error 类型后,这个类型实现了 error() 方法
func TestErrorMethod(t *testing.T) {
err := errors.New("abc")
if err.Error() != "abc" {
t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc")
}
}
这里容易搞混 err 和 err.Error(), 不知道它们有什么区别,这里我们来看一下
func main() {
err := errors.New("sdf")
fmt.Println(reflect.TypeOf(err)) //*errors.errorString
fmt.Println(reflect.ValueOf(err)) //sdf
e := err.Error()
fmt.Println(reflect.TypeOf(e)) //string
fmt.Println(reflect.ValueOf(e)) //sdf
}
New 函数返回一个 error 接口的实例,这个接口是 errorString 的实现,所以 err 是个接口,它的动态类型是 *errors.errorString, 值是实例传入的 errorString 的值 sdf.
func New(text string) error {
return &errorString{text}
}
err 是个接口,error() 就是 err 接口方法集中的 error() 方法,所以这里 err.Error() 就是调用 Error() 方法,把返回值赋给 e,返回值是 string, 所以 e 的类型是 string
type error interface {
Error() string
}
而 e 是实例 err 创建出来的,err 接口这里是 errorString 实现的,所以这里就是传入的 sdf,sdf 赋给 errorString 的 是,Error() 再获取 s, 所以 e 的 值就是 sdf
- ExampleNew_errorf()
有 格式化新建 error 的,即 ExampleNew_errorf 函数,这个函数用了 fmt 包中的 Errorf 函数,这个函数用来用来新建格式化 error,用到了自建的一种格式,即 wrapError struct, 也就是我们在 error 自建类型中讲到的,不过这里不具体展开其实现原理。
// The fmt package's Errorf function lets us use the package's formatting
// features to create descriptive error messages.
func ExampleNew_errorf() {
const name, id = "bimmler", 17
err := fmt.Errorf("user %q (id %d) not found", name, id)
if err != nil {
fmt.Print(err)
}
// Output: user "bimmler" (id 17) not found
}
- TestNewEqual()
有关于 New 函数特点的,我们再来看一下 New 函数的源码
func New(text string) error {
return &errorString{text}
}
它返回时,建立了一个临时变量 errorString 类型,而我们在区分 err 和 error 时知道,New 函数返回时,返回了一个指针,如果忘记了,可以运行
func main() {
fmt.Println(reflect.TypeOf(errors.New("sdf")))
}
所以如果直接比较两个 errors.New(), 哪怕输入的字符串相同,它们也是不相同的两个变量
func main() {
fmt.Println(errors.New("f") == errors.New("f"))
}
所以这里是 flase
, 我们就不能这样判断错误是否相同,我们可以先赋值给一个变量
func main() {
err := errors.New("df")
fmt.Println(err == err)
}
这里 err 是一个变量,所以是 true
, 或者我们可以通过 Error() 方法获取它们的值,再比较
func main() {
fmt.Println(errors.New("f").Error() == errors.New("f").Error())
}
故是 true