天天看点

Go error 接口

Go的标准库函数在发生错误时会返回error类型,比如常用的os.Open函数用来打开文件,错误时返回error:

func Open(name string) (*File, error)
           

那么究竟什么是error类型呢?其实很简单,error是一个接口,该接口只声明了一个方法Error(),返回值是string类型,用以描述错误:

type error interface {
    Error() string
}
           

如何创建自己的error呢?当然可以自己实现这个接口,比如:

package main

import "fmt"

type MyError struct {
    Desc string
}

func (myErr MyError) Error() string {
    return myErr.Desc
}

func doSomething() error {
    // ...
    return MyError{"Logic Error!"}
}

func main() {
    if err := doSomething(); err != nil {
        fmt.Println(err)
    }
}
           

但是更简单的方法是利用errors包中的New()函数直接创建error对象,errors包的内容仅有如下四行代码:

package errors
func New(text string) error { return &errorString{text} }
type errorString struct { text string }
func(e *errorString) Error() string { return e.text }
           

可以看到errors包的内部声明了一个 errorString 结构体,并用其中的text字段来表示错误描述,而不是直接用字符串来表示,这样做的好处是将错误描述封装了起来,防止我们在包外意外地改变错误描述。注意实现 error 接口的是 *errorString,而不是 errorString,这是为了让所有New返回的error对象都不相等:

一版情况下我们很少调用errors.New()来创建error对象,而是通过fmt包中提供的函数Errorf():

package fmt
import "errors"
func Errorf(format string, args ...interface{}) error {
    return errors.New(Sprintf(format, args...))
}
           

可以看到fmt.Errorf()也是通过errors.New()来创建error对象。

*errorString 可能是最简单的error对象,但并不是唯一的error对象,比如syscall包中的Errno也是一个实现了error接口的类型。在Unix实现中,Errno的Error()方法通过查找错误列表返回错误信息:

package syscall
type Errno uintptr
var errors = [...]string {
   : "operation not permitted",
   : "no such file or directory",
    // ...
}
func (e Errno) Error() string {
    if <= int(e) && int(e) < len(errors) {
        return errors[e]
    }
    return fmt.Sprintf("errno %d", e)
}
           

参考自《Go程序设计语言》