错误处理

Golang中的错误类型

程序执行的过程中会发生的异常情况称为错误。比如打开一个文件系统不存在的文件就会发生错误。

Golang中有个专门用来表示错误的接口类型:error

intfloat64等其他数据类型类似,error类型的值也可以拿来赋值给变量,传参给函数,或者作为函数返回值。

举例

我们举个例子,尝试打开一个系统不存在的文件。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

这里我们尝试打开路径为/test.txt的文件。os.Open函数的签名如下:

如果文件打开成功,则Open函数返回值file为该文件的指针,同时返回值errnil。如果读取文件发生异常,比如文件不存在,则返回值err不为nil

在Golang中,如果一个函数/方法会返回错误,一般都是作为最后一个返回值,这里的Open函数返回的错误就是位于第二个位置,也就是最后一个位置。

通常情况下我们都是将函数返回的error字段跟nil比较。如果是nil,则表示无错误,否则表示有错误发生。

前面的程序我们就是将errornil比较,来判断是否正常打开文件。该程序执行结果为:

很明显,该错误信息表示文件/test.txt不存在(我的机器确实不存在该文件,你的机器应该也不会凑巧存在该文件吧)。

error接口类型

Golang中error是一个接口类型,定义如下:

该接口仅包含一个方法Error() string,该方法用来表示错误信息。任何实现了该接口的类型都是错误类型。当我们通过fmt.Println打印一个错误类型的变量时,其实就是调用其Error() string方法获取错误信息并打印。

获取更多错误信息

Golang中的error是一个接口类型,现在我们看下如果从error变量中提取更多错误相关的信息。

前面的例子中我们打印出了错误的描述信息,但是如果我们想获取发生错误的文件路径怎么实现呢?

这肯定是能够实现的,比如我们打印出的错误信息里面就有这个信息,可以解析出来。但是直接从错误文本信息中解析文件路径非常不明智,我们应该寻找更加正规靠谱的方法。

1.断言error具体类型并通过其属性获取信息

我们看下Open函数的官方文档会发现,该函数返回的错误字段的类型为*PathErrorPathError是一个结构体类型,该类型实现了Error() string方法,实现了error接口,因此属于error类型。

这几行源码可以参考https://golang.org/src/os/error.go?s=653:716#L11

Error()函数的实现我们就能理解前面的输出了:

结构体类型PathError的属性Path就是用来表示文件路径的,因此我们可以读取该字段来获取文件路径。

这里我们通过类型断言来获取了error接口内部的具体值。接着我们通过err.Path来获取文件路径。该程序输出为:

2.断言error具体类型并通过其方法获取信息

当我们断言出error内部的具体类型后,不仅可以通过其属性获取更多有用信息,还可以调用其方法来获取信息。我们通过例子来理解下。在Golang标准库中,结构体类型DNSError的定义如下:

该结构体类型有两个方法Timeout() boolTemporary() bool用来表示具体错误原因是超时还是暂时解析失败。下面我们实现一个例子,来断言*DNSError类型,如果断言成功则调用其方法来判断错误原因。

这里我们尝试获取域名golangbot123.com的ip地址,由于该域名是不存在的,因此会报域名解析错误,即net.DNSError类型的错误。我们断言该类型错误,然后调用该类型错误上的方法来判断具体错误原因。该程序运行结果如下:

3.直接判断相等

第三种获取更多错误相关信息的方法就是直接将错误字段跟某个error类型的已有变量做比较。我们还是通过例子来了解下。

filepath包中有个函数Glob用来返回跟某个正则匹配的文件目录。如果正则写的格式有问题会返回一个错误变量ErrBadPatternErrBadPatternfilepath包中定义为:

这里errors.New()用来创建一个error类型的变量。由于这里定义的ErrBadPattern是大写开头的,因此属于导出型变量,我们能够直接在包外访问到该变量。下面我们通过例子来判断下错误是否是ErrBadPattern

这里我们传给Glob的参数是[,格式是错误的(具体为什么是错误的可以去看Glob相关文档)。然后我们直接将返回的错误字段跟变量filepath.ErrBadPattern去比较是否相等,如果相等则证明确实是该错误。该程序执行结果如下:

Golang中基本都是基于这3种方式来提供更多错误相关的细节信息。我们下一节也会基于这几种方式来自定义错误类型。

不要忽略错误

注意,永远不要忽略错误字段。比如下面的程序就忽略了filepath.Glob()返回的错误字段。

由于入参[是格式错误的,因此会返回错误字段,但是我们通过_将该错误忽略了。该程序输出为matched files [],表面上看起来是未匹配到任何字段,其实是发生了错误。因此我们在Golang编程中永远不要随意的忽略一个错误。

Last updated

Was this helpful?