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 关键字

  1. defer 函数会在调用它的函数返回之前被立即调用;
  2. 可以放在函数的任何位置;
  3. 可以使用 defer 定义释放资源函数;
  4. 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 时,会发生如下情况:

  1. 当前执行函数立即终止;
  2. defer 定义的任何函数将会被执行;
  3. 整个程序会终止;

我们来看下关于 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来啦