博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用 Panic、Defer 和 Recover 处理 Go 错误
阅读量:2289 次
发布时间:2019-05-09

本文共 3988 字,大约阅读时间需要 13 分钟。

点击上方蓝色“Golang来啦”关注我哟

加个“星标”,天天 15 分钟,掌握 Go 语言

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 mainimport (      "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 mainimport (      "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 关键字

  1. defer 函数会在调用它的函数返回之前被立即调用;

  2. 可以放在函数的任何位置;

  3. 可以使用 defer 定义释放资源函数;

  4. defer 函数会被执行即使发生报错;

让我们通过一段小程序理解下:

package mainimport "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 时,会发生如下情况:

  1. 当前执行函数立即终止;

  2. defer 定义的任何函数将会被执行;

  3. 整个程序会终止;

我们来看下关于 panic 的例子:

package mainimport "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 mainimport "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 mainimport "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() 函数,主函数继续运行。

推荐阅读:

如果我的文章对你有所帮助,点赞、转发都是一种支持!

给个[在看],是对四哥最大的支持

转载地址:http://fyfnb.baihongyu.com/

你可能感兴趣的文章
dubbo 报错:java.lang.NoClassDefFoundError: org/I0Itec/zkclient/exception/ZkNoNodeException
查看>>
logback的使用和logback.xml详解
查看>>
Linux 快捷键
查看>>
JPA 联合主键配置
查看>>
ObjectAlreadyExistsException:Unable to store Job : '*', because one already exists with thi s ident
查看>>
mybatis & JPA 实体类型属性转换
查看>>
Git 中的 ~ 和 ^
查看>>
第一篇博客,给大家分享java、架构师、大数据、人工智能的学习路线,希望能够帮助到大家
查看>>
18级大数据专家,跟大家漫谈大数据平台架构,你能学到多少?上篇
查看>>
18级大数据专家,漫谈大数据平台安全风险与建设,值得学(下篇)
查看>>
阿里P8终于整理出:Nginx+jvm+MySQL+Docker+Spring实战技术文档
查看>>
腾讯T4专家精心整理:大数据+机器学习+数据挖掘+算法大集结
查看>>
阿里P8终于总结出:SpringBoot+Tomcat+Nginx+Netty面试题及答案
查看>>
阿里P7大牛,深入剖析JVM底层设计原理+高级特性pdf,附46页ppt
查看>>
史上最全141道大数据面试题:Redis+Linux+kafka+Hadoop,附答案
查看>>
一文带你深入理解JVM,看完之后你还敢说你懂JVM吗?颠覆you认知
查看>>
这些大厂面试真题你能答出来,年薪至少30-50W,想不想挑战一下?
查看>>
携程T7用637页PDF,解读十余热门技术领域,八场携程技术沙龙干货
查看>>
开发框架SpringBoot:构建SpringBoot工程+配置文件详解+Actuator
查看>>
6年拉力工作经验,学了阿里P8级架构师的7+1+1落地项目,跳槽阿里年薪直接40W+
查看>>