清水泥沙

golang基础(36.error 接口及其使用)

PHP错误处理以及异常处理一直比较混乱,在 PHP 5 中是通过 error_reporting 函数设置错误报告级别,然后通过 set_error_handler 函数注册全局的错误处理器。PHP中抛弃了错误的报告方式,转而通过抛出将错误当做异常抛出,可以通过try catch语句进行捕获,还可以通过 set_exception_handler 注册全局异常处理器,将应用中未处理的异常统一兜底。

GO语言错误处理机制

GO语言的错误处理机制相对来说比较简洁,它提供了一个标准的error接口。

type error interface { 
    Error() string 
}
其中`error`接口值只有一个标准方法,`Error()`,用于返回错误信息。在大多数函数中一般将一个`error`作为一个返回值,交由上级调用来进行判断。
func Foo(param int) (n int, err error) { 
    // ...
}

然后在调用返回错误信息的函数/方法时,按照如下「卫述语句」模板编写处理代码即可:

n, err := Foo(0)

if err != nil { 
    // 错误处理 
} else {
    // 使用返回值 n 
}

错误消息返回及处理

我们可以使用errors.new()方法返回我们需要自定的错误信息。

func add(a, b *int) (c int, err error) {
    if (*a < 0 || *b < 0) {
        err = errors.New("只支持非负整数相加")
        return
    }
    *a *= 2
    *b *= 3
    c = *a + *b
    return
}

调用这个函数并处理错误的代码如下所示:

x, y := 1, 2
z, err := add(&x, &y)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Printf("add(%d, %d) = %d
", x, y, z)
}

注意到我们在打印错误信息时,直接传入了 err 对象实例,因为 Go 底层会自动调用 err 实例上的 Error() 方法返回错误信息并将其打印出来,就像普通类的 String() 方法一样。以上这种错误处理已经能够满足我们日常编写 Go 代码时大部分错误处理的需求了,事实上,Go 底层很多包进行错误处理时就是这样做的,此外我们还可以通过 fmt.Errorf() 格式化方法返回 error 类型错误,其底层调用的其实也是 errors.New 方法:

func Errorf(format string, a ...interface{}) error {
    return errors.New(Sprintf(format, a...))
}

内置错误类型

除了上面这种最基本的使用 errors.New 方法返回错误信息之外,Go 语言内置的很多包还封装了很多更复杂的错误类型,以 os 包为例,这个包主要与操作平台的文件系统打交道,所以提供了 LinkError、PathError、SyscallError 这些实现了 error 接口的错误类型,以 PathError 为例,其底层类型结构如下:

type PathError struct {
    Op   string
    Path string
    Err  error
}

该错误类型除了组合 error 接口实现 Error() 方法外,还提供了额外的操作类型字段 Op 和文件路径字段 Path 以丰富错误信息,该类型的 Error() 方法实现如下:

func (e *PathError) Error() string { 
    return e.Op + " " + e.Path + ": " + e.Err.Error() 
}

我们可以在调用 os 包方法出错时通过 switch 分支语句判定具体的错误类型进行处理:

fi, err := os.Stat("test.txt")
if err != nil {
    switch err := err.(type) {
    case *os.PathError:
        // do something
    case *os.LinkError:
        // dome something
    case *os.SyscallError:
        // dome something
    case *exec.Error:
        // dome something
    }
} else {
    // ...
}

自定义错误类型

当然,我们也可以仿照 PathError 的实现自定义一些复杂的错误类型,只需要组合 error 接口实现 Error() 方法即可,然后按照自己的需要为自定义类型添加一些属性字段,很简单,这里就不展开介绍了。