[TOC] # error 返回低级错误,不想让程序崩溃 go引入了一个错误处理的标准模式,是error接口,它是go语言内建的接口类型,该接口的定义如下 ~~~ type error interface { Error() string } ~~~ go标准库代码包errors为用户提供如下的方法 ~~~ package errors type errorString struct { text string } func New(text string) error { return &errorString{text} } func (e *errorString) Error() string { return e.text } ~~~ 另一个可以生成error类型值的方法是调用fmt包中的Errorf函数 ~~~ package fmt import "errors" func Errorf(format string, args ...interface{}) error { return errors.New(Sprintf(format, args)) } ~~~ ## 使用 ~~~ func main() { var err error = fmt.Errorf("%s", "this is normal err") fmt.Println(err) var err1 error = errors.New("a normal err1") fmt.Println(err1) } ~~~ ## 应用 ~~~ func test(a, b int) (result int, err error) { if b == 0 { err = errors.New("分母不能为0") } else { result = a / b } return } func main() { result, err := test(10, 2) if err != nil { fmt.Println(err) } else { fmt.Println(result) } } ~~~ ## 类型 类型在已知范围内的错误值其实是最容易分辨的。就拿os包中的几个代表错误的类型os.PathError、os.LinkError、os.SyscallError和os/exec.Error来说,它们的指针类型都是error接口的实现类型,同时它们也都包含了一个名叫Err,类型为error接口类型的代表潜在错误的字段。 如果我们得到一个error类型值,并且知道该值的实际类型肯定是它们中的某一个,那么就可以用类型switch语句去做判断。例如 ~~~ func underlyingError(err error) error { switch err := err.(type) { case *os.PathError: return err.Err case *os.LinkError: return err.Err case *os.SyscallError: return err.Err case *exec.Error: return err.Err } return err } ~~~ 我们还拿os包来说,其中不少的错误值都是通过调用errors.New函数来初始化的,比如:os.ErrClosed、os.ErrInvalid以及os.ErrPermission,等等 好在我们总是能通过错误值的Error方法,拿到它的错误信息。其实os包中就有做这种判断的函数,比如:os.IsExist、os.IsNotExist和os.IsPermission # 根据实际情况给予恰当的错误值? 我们已经知道,构建错误值体系的基本方式有两种,即:创建立体的错误类型体系和创建扁平的错误值列表。 先说错误类型体系。由于在 Go 语言中实现接口是非侵入式的,所以我们可以做得很灵活。比如,在标准库的net代码包中,有一个名为Error的接口类型。它算是内建接口类型error的一个扩展接口,因为error是net.Error的嵌入接口。 net.Error接口除了拥有error接口的Error方法之外,还有两个自己声明的方法:Timeout和Temporary。 net包中有很多错误类型都实现了net.Error接口,比如: 1. \*net.OpError; 2. \*net.AddrError; 3. net.UnknownNetworkError等等。 你可以把这些错误类型想象成一棵树,内建接口error就是树的根,而net.Error接口就是一个在根上延伸的第一级非叶子节点。 同时,你也可以把这看做是一种多层分类的手段。当net包的使用者拿到一个错误值的时候,可以先判断它是否是net.Error类型的,也就是说该值是否代表了一个网络相关的错误。 如果是,那么我们还可以再进一步判断它的类型是哪一个更具体的错误类型,这样就能知道这个网络相关的错误具体是由于操作不当引起的,还是因为网络地址问题引起的,又或是由于网络协议不正确引起的。 当我们细看net包中的这些具体错误类型的实现时,还会发现,与os包中的一些错误类型类似,它们也都有一个名为Err、类型为error接口类型的字段,代表的也是当前错误的潜在错误。 所以说,这些错误类型的值之间还可以有另外一种关系,即:链式关系。比如说,使用者调用net.DialTCP之类的函数时,net包中的代码可能会返回给他一个\*net.OpError类型的错误值,以表示由于他的操作不当造成了一个错误。 同时,这些代码还可能会把一个\*net.AddrError或net.UnknownNetworkError类型的值赋给该错误值的Err字段,以表明导致这个错误的潜在原因。如果,此处的潜在错误值的Err字段也有非nil的值,那么将会指明更深层次的错误原因。如此一级又一级就像链条一样最终会指向问题的根源。