• 13 Go的错误处理


    概述

            在上一节的内容中,我们介绍了Go的接口,包括:定义接口、实现接口、使用接口、空接口等。在本节中,我们将介绍Go的错误处理。在Go语言中,错误处理是一种重要的编程模式,它用于处理可能出现的错误或异常情况。Go语言采用了一种简洁而直接的错误处理方式,通过使用内置的error类型和约定的返回值,开发人员可以有效地处理和传递错误信息。

    errors包

            Go语言中的errors包主要用于进行错误处理,它提供了一些简单的接口和函数,用于创建和操作错误值。下面,我们介绍errors包中一些常见函数的使用方法。

            创建错误:可以使用errors.New()函数来创建一个简单的错误值。它接受一个字符串参数,用于表示错误信息。比如:

              err := errors.New("something is wrong")

            包装错误:可以使用errors.Wrap()函数来包装一个错误值。它接受一个错误值和一个字符串参数,返回一个新的错误值,其中包含了原始错误的详细信息。比如:

              err := errors.Wrap(originalError, "something is wrong")

            获取错误信息:可以使用errors.Unwrap()函数来获取包装错误中的原始错误。它接受一个错误值,返回原始错误。比如:

              originalErr := errors.Unwrap(err)

            判断错误类型:可以使用errors.Is()函数来判断一个错误是否属于特定的类型。它接受一个错误值和一个类型参数,返回一个布尔值表示是否匹配。比如:

              if errors.Is(err, io.EOF)

            错误格式化:可以使用errors.Errorf()函数来创建一个格式化的错误值。它接受一个格式化字符串和变量参数,类似于fmt.Sprintf()函数。比如:

              err := errors.Errorf("something is wrong: %s", errorMessage)

    返回错误

            在Go语言中,通常将函数的最后一个返回值定义为error类型,用于指示函数执行过程中是否发生了错误。如果函数执行成功,该错误值为nil;如果函数执行失败,则将相应的错误值赋给错误变量。这种约定使得函数的调用者可以轻松地检查函数是否返回了错误,并根据需要采取相应的处理措施。

            在下面的示例代码中,divide函数接受两个float64类型的参数,并返回一个float64类型的结果和一个error类型的错误。当除数为零时,函数会返回一个非零的错误值,用于描述错误信息。在main函数中,我们通过检查错误变量err是否为nil来判断函数是否执行成功。如果err不为nil,则表示函数执行失败,并打印相应的错误信息;否则,打印函数执行的结果。

    1. package main
    2. import "fmt"
    3. import "errors"
    4. func divide(a, b float64) (float64, error) {
    5. if b == 0 {
    6. return 0, errors.New("divisor cannot be zero")
    7. }
    8. return a / b, nil
    9. }
    10. func main() {
    11. result, err := divide(10, 2)
    12. if err != nil {
    13. fmt.Println("Error:", err)
    14. } else {
    15. // 输出:Result: 5
    16. fmt.Println("Result:", result)
    17. }
    18. result, err = divide(10, 0)
    19. if err != nil {
    20. // 输出:Error: divisor cannot be zero
    21. fmt.Println("Error:", err)
    22. } else {
    23. fmt.Println("Result:", result)
    24. }
    25. }

            除了直接返回错误值外,还可以使用多返回值的方式在函数内部进行错误处理。在下面的示例代码中,我们使用一个额外的变量来指示函数是否执行成功。

    1. package main
    2. import "fmt"
    3. import "errors"
    4. func divide(a, b float64) (float64, bool, error) {
    5. if b == 0 {
    6. return 0, false, errors.New("divisor cannot be zero")
    7. }
    8. if b == 1 {
    9. return 0, false, nil
    10. }
    11. return a / b, true, nil
    12. }
    13. func main() {
    14. result, success, err := divide(10, 1)
    15. if err != nil {
    16. fmt.Println("Error:", err)
    17. } else if !success {
    18. // 输出:divisor cannot be 1
    19. fmt.Println("divisor cannot be 1")
    20. } else {
    21. fmt.Println("Result:", result)
    22. }
    23. }

    抛出异常

            在Go语言中,抛出异常可以使用panic函数。panic函数是一个内置函数,用于表示发生了一个无法恢复的错误。当panic函数被调用时,当前函数的执行会立即停止,并且向上递归调用栈,直到找到适当的defer语句或函数返回。

            panic函数通常用于处理无法处理的错误情况,比如:内存溢出、空指针引用等。当panic函数被调用时,它会传递一个字符串作为参数,表示发生错误的原因,这个字符串可以被捕获并用于错误处理或日志记录。

            在下面的示例代码中,我们用panic函数抛出了异常。在执行panic函数之前,会打印输出字符串“before panic”。在执行panic函数之后,main函数的执行会立即停止,故不会继续执行后面的打印语句。

    1. package main
    2. import "fmt"
    3. func main() {
    4. // 输出:before panic
    5. fmt.Println("before panic")
    6. // 抛出异常
    7. panic("an exception occured")
    8. // 以下语句不会被执行
    9. fmt.Println("after panic")
    10. }

    捕获异常

            在Go语言中,捕获异常可以使用recover函数和defer关键字。

            recover函数是一个内建函数,用于从一个panic异常中恢复并继续执行程序。当程序遇到panic时,它会中断当前的执行流程并开始向上层调用栈传播panic,直到被捕获或程序终止。recover函数允许在defer关键字修饰的函数中捕获并处理panic异常,以便程序可以继续执行而不会终止。

            defer关键字用于延迟执行一个函数调用,直到包含它的函数返回之前执行。被defer修饰的函数调用会被推入一个栈中,等到包含它的函数返回时,该函数调用才会被从栈中弹出并执行。defer关键字通常用于在函数返回之前执行一些清理操作,比如:关闭文件、释放资源、打印日志等。它可以用于确保在函数执行结束时,相关的资源被正确释放,避免资源泄漏问题。

            注意:recover函数只能在defer函数中使用,不能在其他上下文中使用。当在defer函数中调用recover时,它会停止panic传播并返回panic的值(如果有的话)。如果没有panic发生,recover函数会返回nil。

            在下面的示例代码中,我们使用defer关键字和匿名函数来创建一个defer函数。在defer函数中,我们调用recover函数来捕获panic异常。如果有panic发生,我们会打印出相应的错误信息。

    1. package main
    2. import "fmt"
    3. func main() {
    4. defer func() {
    5. if r := recover(); r != nil {
    6. // 捕获到异常,输出:an exception caught: an exception occured
    7. fmt.Println("an exception caught:", r)
    8. }
    9. }()
    10. // 输出:before panic
    11. fmt.Println("before panic")
    12. // 抛出异常
    13. panic("an exception occured")
    14. // 以下语句不会被执行
    15. fmt.Println("after panic")
    16. }

  • 相关阅读:
    操作步骤:EasyCVR平台国标GB设备如何正确配置告警预案?
    使用JSAPl来做一个倒计时的效果
    如何为企业找到合适的RPA顾问
    为什么大企业都更愿意采用私有化部署?
    数据结构难题小解析
    力扣:474.一和零
    互联网Java工程师面试题·Java 面试篇·第一弹
    「go查漏补缺」命名规则以及 GROM 结构体的应用
    DC电源模块对效率有什么要求?
    Linux 线程概念&线程控制
  • 原文地址:https://blog.csdn.net/hope_wisdom/article/details/134483035