今天我们一起了解下go的异常处理吧。
1. error
Go语言内置了一个简单的错误接口作为一种错误处理机制,接口定义如下:
1 | type error interface { |
它包含一个 Error()
方法,返回值为string
Go的error构造有两种方式,分别是
第一种:errors.New()
1 | package main |
执行结果:
1 | This is an error |
第二种:fmt.Errorf()
1 | package main |
执行结果:
1 | This is an error |
除了直接使用Go自带的方法,还可以自定义错误。下面以自然数函数作为例子:
1 | type NotNature float64 |
执行结果:
1 | 1 <nil> |
需要注意一下几点:
1.如果函数需要处理异常,通常将error作为多值返回的最后一个值,返回的error值为nil则表示无异常,非nil则是有异常。
2.一般先用if语句处理error!=nil,正常逻辑放if后面。
Go语言的error代表的并不是真“异常”,只是通过返回error来表示错误信息,换句话说,不是运行时错误范围预定义的错误,某种不符合期望的行为并不会导致程序无法运行(自然数函数例子),都应使用error进行异常处理。当程序出现重大错误,如数组越界,才会将其当成真正的异常,并用panic来处理。
2. panic
Go不使用try...catch方法来处理异常,而是使用panic和recover
先上代码举一个简单的例子
1 | package main |
输出:
1 | Hello,Go! |
可以看到,panic后面的程序不会被执行了。但是我们捕捉异常并不是为了停止程序(一般情况),而是为了让程序能正常运行下去,这时候就到recover出场了。
1 | package main |
输出:
1 | 1 |
可以看到,f函数一开始正常打印,当遇到panic,就跳到defer函数,执行defer函数里的内容
需要注意的是:defer函数里打印的err其实就是panic里面的内容。
下面详细介绍一下panic和recover的原理。首先来看一下panic和recover的官方定义
1 | func panic(v interface{}) |
内置函数 panic 会停止当前 goroutine 的正常执行。当函数F调用panic时,F的正常执行立即停止。任何被F延迟执行的函数都将以正常的方式运行,然后F返回其调用者。对调用方G来说,对F的调用就像调用panic一样,终止G的执行并运行任何延迟的函数。直到执行goroutine中的所有函数都按逆序停止。此时,程序将以非0退出代码终止。此终止序列称为panicking,可由内置函数recover控制。
1 | func recover() interface{} |
recover内置函数允许程序管理panicking的goroutine的行为。在defer函数(但不是它调用的任何函数)内执行恢复调用,通过恢复正常执行来停止panicking序列,并检索传递给panic调用的错误值。如果在defer函数之外调用recover,则不会停止panicking的序列。在这种情况下,或者当goroutine不panicking时,或者提供给panic的参数是nil,recover返回nil。因此,recover的返回值报告goroutine是否panicking
ps:defer和recover必须在panic之前定义,否则无效。
3. 源码分析
errors.New的定义如下
1 | // src/errors/errors.go |
1.New函数返回格式为给定文本的错误
2.即使文本是相同的,每次对New的调用都会返回一个不同的错误值。
参考
[1] https://github.com/datawhalechina/go-talent/blob/master/9.异常处理.md