Go语言错误处理机制详解
Go 语言的错误处理思过程如下:
Go 语言内置错误接口类型 error。任何类型只要实现 Error() string 方法,都可以传递 error 接口类型变量。Go 语言典型的错误处理方式是将 error 作为函数最后一个返回值。在调用函数时,通过检测其返回的 error 值是否为 nil 来进行错误处理。
自定义错误的处理方法如下:
异常是指发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误(untrapped error)。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理,而是由操作系统进行异常处理。
错误的分类如下图所示:
图 1 错误的分类
在 Go 语言中对于错误提供了两种处理机制:
对错误的处理也有两种方法,一种是通过返回一个错误类型值来处理错误,另一种是直接调用 panic 抛出错误,退出程序。
Go 是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。此种错误行为将导致程序异常退出。其表现出的行为就和直接调用 panic 一样:打印出函数调用栈信息,并且终止程序执行。
在实际的编程中,error 和 panic 的使用应该遵循以下原则:
- 一个可能造成错误的函数,需要返回值中返回一个错误接口,如果调用是成功的,错误接口将返回 nil,否则返回错误;
- 在函数调用后需要检查错误,如果发生错误,则进行必要的错误处理。
错误接口
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 行,将 errorString 结构体实例化,并赋值错误描述的成员。
- 第 6 行,声明 errorString 结构体,拥有一个成员,描述错误内容。
- 第 10 行,实现 error 接口的 Error() 方法,该方法返回成员中的错误描述。
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
在以上代码中:- 第 7 行,预定义除数为 0 的错误。
- 第 8 行,声明除法函数,输入被除数和除数,返回商和错误。
- 第 10 行,在除法计算中,如果除数为 0,计算结果为无穷大,为了避免这种情况,对除数进行判断,并返回商为 0 和除数 0 的错误对象。
- 第 14 行,进行正常的除法计算,没有发生错误时,错误对象返回 nil。
自定义错误的处理方法如下:
- 在多个返回值的函数中,error 通常作为函数最后一个返回值;
- 如果一个函数返回 error 类型变量,则先用 if 语句处理 error!= nil 的异常场景,正常逻辑放到 if 语句块的后面,保持代码平坦;
- defer 语句应该放到 error 判断的后面,不然有可能产生 panic;
- 在错误逐级向上传递的过程中,错误信息应该不断丰富和完善,而不是简单地抛出下层调用的错误;
错误和异常
从广义上来说,错误是指发生非期望的行为;从狭义上来说,错误是指发生非期望的已知行为,这里的已知是指错误的类型是预料并定义好的。异常是指发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误(untrapped error)。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理,而是由操作系统进行异常处理。
错误的分类如下图所示:
图 1 错误的分类
在 Go 语言中对于错误提供了两种处理机制:
- 通过函数返回错误类型的值来处理错误。
- 通过 panic 打印程序调用栈,终止程序执行来处理错误。
对错误的处理也有两种方法,一种是通过返回一个错误类型值来处理错误,另一种是直接调用 panic 抛出错误,退出程序。
Go 是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。此种错误行为将导致程序异常退出。其表现出的行为就和直接调用 panic 一样:打印出函数调用栈信息,并且终止程序执行。
在实际的编程中,error 和 panic 的使用应该遵循以下原则:
- 程序发生的错误导致程序不能容错继续执行,此时程序应该主动调用 panic 或由运行时抛出 panic。
- 程序虽然发生错误,但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,或者在可能发生运行时错误的非关键分支上使用 recover 捕获 panic。