via:
https://medium.com/technofunnel/error-handling-in-golang-with-panic-defer-and-recover-d77db7ae3875
作者:Mayank Gupta
四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!
文章源自 Medium,点赞超过 700+。
原文如下:
这篇文章主要会与大家介绍 Go 语言的错误处理。
我们将会讨论关于 Go 语言创建和捕获自定义、运行时错误的一些简单方法。Go 提供了简单方法实现。
Go 提供了简单的错误接口,每个返回错误都必须实现这个接口。
type error interface {
Error() string
}
创建用户自定义错误
我们可以使用 Go 语言创建简单的用户自定义错误,就像下面这样:
package main
import (
"errors"
"fmt"
)
func calculateArea(radius int) (int, error) {
if radius < 0 {
return 0, errors.New("Provide Positive Number")
}
return radius * radius, nil
}
上面代码很简单,求一个圆的面积,必须保证 radius 是正数。如果参数是负数,将会返回 0 与 用户自定义错误,该自定义错误使用 errors.New() 生成。该函数返回 error 类型的对象并可携带用户自定义错误信息。我们使用负数调用函数,这样调用时就能返回错误:
package main
import (
"errors"
"fmt"
)
func calculateArea(radius int) (int, error) {
if radius < 0 {
return 0, errors.New("Provide Positive Number")
}
return radius * radius, nil
}
func main() {
areaValue, err := calculateArea(-1)
if err != nil {
fmt.Println("Error Encountered...")
return
}
fmt.Println(areaValue)
}
在 main 函数里,我们使用负数调用函数 calculateArea()。参数是负数,所以会返回 error 对象。函数执行完会返回两个值,第一个是面积,第二个是 error 对象。上面的代码,我们会检查函数返回的 error 对象是否是 nil,如果是,函数将会继续执行;否则,错误返回并打印错误信息。
创建自定义函数时,我们应当保证能返回正常值和错误状态。上面的代码展示了处理错误的场景。函数抛出错误的场景有很多,我们也需要研究如何处理这些错误。让我们一起来深入研究下这些方法。
关键字 Defer、Panic 和 Recover
使用 defer 关键字
- defer 函数会在调用它的函数返回之前被立即调用;
- 可以放在函数的任何位置;
- 可以使用 defer 定义释放资源函数;
- defer 函数会被执行即使发生报错;
让我们通过一段小程序理解下:
package main
import "fmt"
func returnMessage() {
fmt.Println("This is Simple Defer Function Execution")
}
func main() {
defer returnMessage()
fmt.Println("This is line One")
fmt.Println("This is line Two")
fmt.Println("This is line Three")
}
上面的代码在 main() 函数里使用 defer 关键字定义 returnMessage() 函数调用。一旦主函数执行,在主函数返回之前就会执行 returnMessage() 调用。输出如下:
即使调用 returnMessage() 函数的代码写在主函数的第一行,实际却是在主函数返回之前发生调用。这就是 Go 语言里面 defer 关键字的工作原理。
使用 Panic 关键字
panic 可以用来终止程序并且可以自定义错误信息。当发生 panic 时,会发生如下情况:
- 当前执行函数立即终止;
- defer 定义的任何函数将会被执行;
- 整个程序会终止;
我们来看下关于 panic 的例子:
package main
import "fmt"
func executePanic() {
panic("This is Panic Situation")
fmt.Println("The function executes Completely")
}
func main() {
executePanic()
fmt.Println("Main block is executed completely...")
}
上面的代码,在 executePanic() 函数里调用了 panic 函数,一旦 panic 执行,整个程序终止,所以输出如下:
当 panic 函数被调用时,程序会在第 6 行代码退出。panic 函数是另外一种提示发生错误并终止程序的方式,并且还可以自定义错误信息。
defer 与 panic 一起使用
前面说过,如果程序发生 panic,将会调用所有与当前执行线程相关的 defer 函数。defer 函数可以用来释放资源。defer 函数将会在当前执行函数终止之前调用。
一起来看下例子:
package main
import "fmt"
func recoveryFunction() {
fmt.Println("This is recovery function...")
}
func executePanic() {
defer recoveryFunction()
panic("This is Panic Situation")
fmt.Println("The function executes Completely")
}
func main() {
executePanic()
fmt.Println("Main block is executed completely...")
}
上面的代码,在 executePanic() 函数里定义了 defer 函数和 panic 函数。看下输出:
上面的代码,当执行到 panic 函数时,会立即调用 defer 函数。从执行情况可以看出,defer 函数在程序终止之前会被调用。一旦发生 panic ,所有 defer 函数都会在程序终止之前被调用。
使用 Recovery
一旦发生 panic,程序将会终止。然而在实际生产环境中,发生错误终止的情况是不允许的。我们需要一种从错误中恢复的机制,通过恢复代码避免程序的意外终止。
无论执行函数是否会不会发生 panic,函数返回时,defer 函数总是会被执行。我们可以在 defer 函数中编写恢复代码。
检测 panic 情况
在 defer 定义的函数中,我们需要检测程序执行时是否发生过 panic 的情况。为了能够检测出,我们需要执行 recover 函数。一旦我们执行了 recover 函数,就可以接收到 panic 函数传递的错误信息。这些错误信息作为 panic 的返回输出到 recover 函数。我们不允许正在执行的程序发生意外终止,而是要重新获得对程序的控制。程序控制权重新交还给调用函数,这样调用函数便可以接着向下继续执行。一起来看下例子:
package main
import "fmt"
func recoveryFunction() {
if recoveryMessage := recover(); recoveryMessage != nil {
fmt.Println(recoveryMessage)
}
fmt.Println("This is recovery function...")
}
func executePanic() {
defer recoveryFunction()
panic("This is Panic Situation")
fmt.Println("The function executes Completely")
}
func main() {
executePanic()
fmt.Println("Main block is executed completely...")
}
上面的代码,在 defer 函数内部,我们调用了 recover 函数,该函数返回 panic 抛出的错误信息。因为我们使用了 recover 函数,所以程序并不会立即终止。相反,程序的控制权将会返回给主函数并得以继续执行。看下输出:
我们可以看到,程序并未异常终止。调用函数正常执行并返回 main() 函数,主函数继续运行。
转自:微信公众叫 Golang来啦