首页 > 编程笔记

Go语言错误处理机制详解

Go 语言的错误处理思过程如下:

错误接口

error 是 Go 语言系统声明的接口类型,语法格式如下:
type error interface {
    Error() string
}
所有符合 Error() string 格式的方法,都能实现错误接口,Error() 方法返回错误的具体描述,使用者可以通过该字符串知道发生了什么错误。

Go 语言内置错误接口类型 error。任何类型只要实现 Error() string 方法,都可以传递 error 接口类型变量。Go 语言典型的错误处理方式是将 error 作为函数最后一个返回值。在调用函数时,通过检测其返回的 error 值是否为 nil 来进行错误处理。

自定义错误

返回错误前,需要定义会产生哪些可能的错误,在 Go 语言中,使用 errors 包进行错误的定义,格式如下:
var err = errors.New("this is an error")
错误字符串由于相对固定,一般在包作用域声明,应尽量减少在使用时直接使用 errors.New 返回。

1) errors包

Go 语言的 errors 中对 New 的定义非常简单,例如:
//创建错误对象
func New(text string) error {
    return &errorString{text}
}
//错误字符串
type errorString struct {
    s string
}
//返回发生何种错误
func (e *errorString) Error() string {
    return e.s
}
在以上代码中:

2) 在代码中使用错误定义

例如定义一个除法函数,当除数为 0 时,返回一个预定义的除数为 0 的错误。
package main
import (
    "errors"
    "fmt"
)
//定义除数为0的错误
var errDivisionByZero = errors.New("division by zero")
func div(dividend, divisor int) (int, error) {
    //判断除数为0的情况并返回
    if divisor == 0 {
        return 0, errDivisionByZero
    }
    //正常计算,返回空错误
    return dividend / divisor, nil
}
func main() {
    fmt.Println(div(1, 0))
}
运行结果为:

division by zero

在以上代码中:
自定义错误的处理方法如下:
  1. 在多个返回值的函数中,error 通常作为函数最后一个返回值;
  2. 如果一个函数返回 error 类型变量,则先用 if 语句处理 error!= nil 的异常场景,正常逻辑放到 if 语句块的后面,保持代码平坦;
  3. defer 语句应该放到 error 判断的后面,不然有可能产生 panic;
  4. 在错误逐级向上传递的过程中,错误信息应该不断丰富和完善,而不是简单地抛出下层调用的错误;

错误和异常

从广义上来说,错误是指发生非期望的行为;从狭义上来说,错误是指发生非期望的已知行为,这里的已知是指错误的类型是预料并定义好的。

异常是指发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误(untrapped error)。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理,而是由操作系统进行异常处理。

错误的分类如下图所示:

图 1 错误的分类

在 Go 语言中对于错误提供了两种处理机制:
对错误的处理也有两种方法,一种是通过返回一个错误类型值来处理错误,另一种是直接调用 panic 抛出错误,退出程序。

Go 是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。此种错误行为将导致程序异常退出。其表现出的行为就和直接调用 panic 一样:打印出函数调用栈信息,并且终止程序执行。

在实际的编程中,error 和 panic 的使用应该遵循以下原则:

推荐阅读