Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Go 语言异常处理

Go 语言异常处理

作者头像
FunTester
发布于 2025-01-23 07:55:39
发布于 2025-01-23 07:55:39
3800
代码可运行
举报
文章被收录于专栏:FunTesterFunTester
运行总次数:0
代码可运行

JavaC# 等编程语言中,错误处理通常是通过 try-catch 机制来管理的。当程序在 try 块中遇到错误时,catch 块会捕获该错误,并执行相应的处理逻辑。这种机制为处理异常提供了一种结构化的方法,确保即使在发生错误的情况下,应用程序也不会意外崩溃。

与此不同,Go 语言采用了一种完全不同的错误处理方式。在 Go 中,没有传统意义上的异常处理机制。相反,Go 将错误视为函数的返回值之一。这意味着在调用函数后,开发者需要主动检查是否返回了错误,并根据情况决定如何处理它。这种方法更加强调显式的错误处理,而不是像 try-catch 那样隐式的异常处理。这不仅使代码逻辑更为清晰,还鼓励了更好的错误管理实践。

下面我们将探讨几种在 Go 语言中处理错误的技巧,帮助你更轻松地管理代码中的错误。

内置errors

Go 语言的错误处理机制以其内置的 error 类型为基础。error 类型是一个接口,它只有一个方法:Error() 。这个方法返回一个描述错误的字符串。任何实现了 Error() 方法的类型都被视为 error 类型。这种设计允许开发者轻松创建自己的自定义错误类型,以适应具体的需求。

这种简单而灵活的设计,使得 Go 的错误类型既强大又易于使用。它完全符合 Go 语言的设计哲学:直观简洁。通过这种方式,错误处理可以自然地融入到 Go 代码的整体结构中,不仅保持了代码的简洁性,还确保了错误处理的有效性。

下面是 error 接口的定义,PS:后面自定义错误类型也会用到这个。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type error interface {
    Error() string
}

Go 标准库中的 errors 包提供了一种简单的方法来创建错误实例,使用的是 errors.New() 函数。这个函数接受一个字符串参数,并返回一个包含该消息的错误对象。由于这种方式非常直接,它鼓励开发者创建易于理解的错误消息,使得错误定位和处理更加简单明了。

Go 语言中,error 类型的使用通常伴随着函数调用后的即时检查。这种方式要求开发者在每次函数调用后都要明确地检查是否发生了错误。这不仅可以确保在错误发生时立即处理,还能防止错误在未被察觉的情况下传播,进而避免在执行流程的后期引发更严重的问题。

这种显式的错误处理方法虽然要求在代码中多写几行,但它带来了更高的代码可读性和更少的隐藏错误的风险,使程序更加健壮和可靠。

下面是一个示例,说明如何实现除法运算的错误处理,具体解决除以零的情况。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "errors"  
    "fmt")  
  
func divide(a, b int) (int, error) {  
    if b == 0 {  
       return 0, errors.New("被除数不能为零, 请检查")  
    }  
    return a / b, nil  
}  
  
func main() {  
    result, err := divide(4, 0)  
    if err != nil {  
       fmt.Println("Error:", err)  
       return  
    }  
    fmt.Println("Result:", result)  
}

控制台打印结果 Error: 被除数不能为零, 请检查

在这个示例中,divide 函数首先检查除数 b 是否为 0。如果是,则通过 errors.New() 函数创建一个错误,并返回给调用者。同时,函数返回一个默认值 0,表示运算失败。反之,如果 b 不为 0,函数则返回 a 除以 b 的结果,以及 nil 表示没有错误。

main 函数中,divide 被调用,并检查返回的 error 是否为 nil。如果发生错误(即 b 为 0),程序将输出错误信息并退出。否则,程序将继续运行并输出除法结果。

包装 error

错误包装是一种为错误信息添加额外上下文的技术,使错误更容易调试和理解。通过为错误添加更多的上下文信息,开发者能够更准确地判断错误发生的原因和位置,这在复杂的代码库中尤为重要。

在 Go 语言的早期版本中,虽然可以通过拼接字符串的方式添加上下文,但这种方法通常会丢失原始错误的具体信息。为了解决这一问题,Go 1.13 引入了 fmt.Errorf() 函数,使得错误包装变得更加简洁和强大。

fmt.Errorf() 函数允许在保留原始错误的同时,为其添加更多有用的上下文信息。这样不仅可以将错误传递给上层调用者,还能为错误信息提供更多细节,以便在出现问题时更容易定位问题的根源。通过这种方式,错误包装能够显著提高代码的可维护性和调试效率。

我们可以这样使用 fmt.Errorf() 进行错误包装:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "errors"  
    "fmt")  
  
func divide(a, b int) (int, error) {  
    if b == 0 {  
  return 0, fmt.Errorf("除法运算error,  %w", errors.New("被除数不能为0"))
    }  
    return a / b, nil  
}  
  
func main() {  
    result, err := divide(4, 0)  
    if err != nil {  
       fmt.Println("Error:", err)  
       return  
    }  
    fmt.Println("结果:", result)  
}

控制台打印:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Error: 除法运算error,  被除数不能为0

错误包装在开发中发挥了重要作用,尤其是在构建更具信息量的错误报告系统时。通过在错误信息中加入详细的上下文,您可以显著提高系统的可读性和调试效率。这种做法不仅能使错误信息更加直观,还能提供关于错误发生的情况和潜在失败原因的宝贵见解。

具体来说,当错误信息包含了更多背景信息时,开发者可以更容易地理解错误的上下文,从而迅速定位问题的根源。例如,错误包装可以显示错误发生的函数名、参数值以及导致错误的具体条件。这种细致的信息有助于在调试过程中快速发现并修复问题,减少了排查错误的时间。

此外,错误包装还可以帮助团队成员之间更好地沟通和协作。详细的错误信息使得团队在讨论问题时可以更精确地描述问题的性质,从而更高效地制定解决方案。这种透明的错误报告方式对于长期维护和迭代开发尤为重要,它使得系统的错误处理更加可靠和易于管理。

自定义error

创建自定义错误类型允许您为错误提供额外的上下文和功能,从而使错误处理更加灵活和有用。当实现 error 接口时,您可以构建更复杂的错误类型,提供对错误的详细见解,这在需要错误消息之外的其他信息时特别有用。

通过定义自定义错误类型,您可以将错误信息与其他相关的数据和行为结合起来。这种方法使您能够创建具有特定属性和方法的错误类型,使得错误报告更加丰富和精确。例如,您可以定义一个错误类型,该类型不仅包含错误消息,还包括错误代码、发生错误的时间、或额外的上下文信息。这样,当错误发生时,您可以获得更全面的错误信息,帮助更好地理解问题的背景和解决方案。

下面是一个演示的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "fmt"  
)  
  
type FunTesterError struct {  
    Reason string  
}  
  
func (e *FunTesterError) Error() string {  
    return fmt.Sprintf("%s is fun", e.Reason)  
}  
  
func divide(a, b int) (int, error) {  
    if b == 0 {  
       return 0, &FunTesterError{Reason: "I am fun"}  
    }  
  
    return a / b, nil  
}  
  
func main() {  
    data, err := divide(8, 0)  
    if err != nil {  
       fmt.Println("Error:", err)  
       return  
    }  
    fmt.Println("结果:", data)  
}

这段 Go 代码演示了如何使用自定义错误类型来改进错误处理。首先,定义了一个 FunTesterError 类型,它实现了 error 接口,并包含一个 Reason 字段。Error() 方法返回格式化的错误信息。divide 函数尝试对两个整数进行除法运算,如果除数 b 为 0,则返回一个 FunTesterError 错误。main 函数调用 divide 函数,并根据是否返回错误来输出相应的错误信息或运算结果。当除数为 0 时,错误消息 "I am fun is fun" 会被打印。这样,自定义错误类型帮助提供了更具描述性的错误信息,便于调试和理解。

error 日志

记录错误是调试和监控应用程序的关键实践之一。Go 语言中的 log 包提供了一种简单而有效的方法来记录错误和其他重要消息。

通过记录错误,您可以实时监控应用程序的状态,及时发现并响应出现的问题。此外,记录的错误信息可以帮助您识别可能预示更深层次问题的模式,从而采取预防措施。这样的记录机制对于维持应用程序的健康和性能尤为重要,尤其是在生产环境中,它能帮助您跟踪和解决潜在的故障,确保应用程序的稳定性和可靠性。

日志记录实现的库比较多,我就用内置的 log 来打印日志了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "errors"  
    "fmt"
    "log")  
  
func divide(a, b int) (int, error) {  
    if b == 0 {  
       return 0, errors.New("被除数不能为零, 请检查")  
    }  
    return a / b, nil  
}  
  
func main() {  
    result, err := divide(4, 0)  
    if err != nil {  
       log.Printf("Error: %s\n", err)  
       return  
    }  
    fmt.Println("Result:", result)  
}

此处省略控制台信息。

记录错误不仅有助于调试和监控应用程序,还对主动监控和警报系统至关重要。通过日志分析,您可以识别潜在的问题模式和趋势,及时采取措施进行干预。这种方法使您能够在问题恶化之前进行修复,从而减少停机时间并提升用户体验。

详细的错误日志可以帮助您发现系统中的异常行为、性能瓶颈或其他潜在故障,并触发预设的警报。这种主动监控机制使您能够在问题发生初期做出响应,避免严重的系统故障,确保应用程序的平稳运行和用户的持续满意。

panic 和 recovery

虽然 Go 的主要错误处理机制依赖于将错误作为返回值处理,但 Go 语言也提供了 panicrecover 机制,用于处理无法通过常规错误处理解决的异常情况。

panic 用于处理程序遇到的不可恢复的错误或严重故障,例如编程错误或致命错误。当 panic 被触发时,程序的正常执行将被中断,控制权会转移到最接近的 defer 语句,进行资源清理,然后程序终止执行。这种机制主要用于处理那些程序无法继续运行的情况,如数组越界或空指针引用等严重错误。

不过,Go 还提供了 recover 函数,用于从 panic 中恢复控制权。recover 只能在 defer 调用中使用,它可以捕获 panic 产生的异常,使程序能够在出现严重错误时以受控的方式继续执行。通过这种机制,开发者可以处理意外的崩溃并恢复程序的正常状态,从而提高程序的健壮性和稳定性。

panic

在 Go 语言中,panic 是一个内置函数,用于立即停止程序的正常控制流。当 panic 被触发时,程序会立即中断当前函数的执行,开始展开调用堆栈,并执行所有沿途的 defer 函数。这种机制用于处理严重错误或异常情况,确保程序在遇到无法继续执行的错误时能够及时停止。

具体来说,当函数调用 panic 时:

  1. 当前函数的执行会被立即停止。
  2. 程序会开始逐层展开堆栈,依次执行每个堆栈帧中的 defer 语句。这些 defer 语句通常用于清理资源或执行必要的清理工作。
  3. 如果 panic 没有被 recover 捕获,程序将继续向上层堆栈展开,直到程序终止。最终,程序会输出堆栈跟踪信息,这对调试非常有用,帮助开发者定位和解决引发 panic 的根本原因。

这种机制允许开发者在遇到无法恢复的错误时,快速停止程序并进行调试,同时提供有用的错误上下文和堆栈信息。然而,应谨慎使用 panic,通常仅在遇到真正无法恢复的错误时使用,日常错误处理应优先依赖于返回值和 error 类型。

下面是个使用 panic 的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "fmt"  
)  
  
func divide(a, b int) int {  
    if b == 0 {  
       panic("被除数不能为零, 请检查")  
    }  
    return a / b  
}  
  
func main() {  
    fmt.Println(divide(4, 0))  
}

当我们运行次代码时,程序会执行 panic 方法,导致运行中断。下面是控制台打印信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
panic: 被除数不能为零, 请检查

goroutine 1 [running]:
main.divide(...)
        /Users/oker/GolandProjects/funtester/test/ttt/main.go:9
main.main()
        /Users/oker/GolandProjects/funtester/test/ttt/main.go:15 +0x30

recover

为了处理 panic 并允许程序在遇到严重错误后继续运行,Go 提供了 recover() 函数。recover 只能在 defer 函数中使用,它允许在 panic 发生后恢复控制权,从而防止程序意外终止。

具体使用方式如下:

  1. 定义 defer 函数:在 defer 函数中调用 recover()defer 确保这些函数在当前函数的执行结束时被调用,无论是正常返回还是因 panic 中断。
  2. **调用 recover 捕获 panic**:在 defer 函数内部调用 recover(),它将检查是否有 panic 发生。如果有,recover 会捕获到 panic 的值,并恢复程序的正常控制流。
  3. 防止程序终止:通过 recover 捕获到 panic 后,程序可以继续执行而不会终止。这使得程序在遇到不可预见的错误时,能够进行必要的清理或执行后续操作。

以下是一个示例代码,展示了如何在 defer 函数中使用 recover 来捕获和处理 panic

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main  
  
import (  
    "fmt"  
    "log")  
  
func divide(a, b int) int {  
    if b == 0 {  
       panic("被除数不能为零, 请检查")  
    }  
    return a / b  
}  
  
func main() {  
    defer func() {  
       if r := recover(); r != nil {  
          log.Printf("从 panic 中恢复: %v", r)  
       }  
    }()  
    fmt.Println(divide(4, 0))  
    fmt.Println("程序正常退出")  
}

这个 Go 代码示例演示了如何使用 panicrecover 来处理严重错误并恢复程序的控制流。在 divide 函数中,如果除数为 0,则调用 panic 触发严重错误。main 函数通过 defer 定义一个匿名函数,该函数在 panic 发生时使用 recover 捕获错误,并通过 log.Printf 输出恢复信息。由于 panic 发生时,divide 函数中的错误信息被捕获,程序不会终止,而是继续执行 defer 中的恢复逻辑。最终,fmt.Println("程序正常退出") 不会执行,因为 panic 打断了正常流程。

下面是控制台打印信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
从 panic 中恢复: 被除数不能为零, 请检查

panic 和 recovery最佳实践

panic 适用于指示程序中不可恢复的严重错误,如数组越界、空指针引用或其他在正确代码中不应出现的情况。这些错误通常表示程序的状态已不再有效,因此应尽快中止执行。避免将 panic 用于常规错误处理,因为它会中断程序的正常流程。

recover 用于处理 panic 并允许程序在发生严重故障后继续运行。在需要清理资源、记录错误信息或尽可能恢复程序状态时,recover 提供了一个有效的机制。它应在 defer 函数中使用,以确保在 panic 发生时能够正确捕获和处理,避免程序直接终止。通过这种方式,您可以在发生严重错误时执行必要的清理工作,并尽可能恢复程序的正常运行。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go语言实战:错误处理和panic_recover之自定义错误类型
Go语言是一种现代的编程语言,它具有简洁的语法和强大的功能。在Go语言中,错误处理是一个重要的主题。Go语言提供了一种简洁的错误处理方式,即通过返回一个错误值来表示一个函数调用是否成功。此外,Go语言还提供了panic和recover机制,用于处理运行时错误。
阿珍
2025/02/22
900
Go语言实战:错误处理和panic_recover之自定义错误类型
Golang深入浅出之-Go语言 defer、panic、recover:异常处理机制
Go语言通过defer、panic和recover三个关键字构建了一种独特的异常处理机制。它们协同工作,使得Go程序能够优雅地处理运行时错误和异常情况。本文将深入浅出地解析这三个关键字的用法、特点以及常见问题与易错点,并通过代码示例进行演示。
Jimaks
2024/04/24
3.9K0
Go 语言常见错误——异常处理
在 Go 语言中,异常处理与传统的面向对象语言有所不同,主要通过返回错误值的方式来处理程序中的异常情况。虽然这种方式简洁明了,但在实际应用中,开发者常常会忽视错误处理的重要性,导致程序在运行时出现潜在问题或不易察觉的漏洞。
FunTester
2025/03/19
740
Go 语言常见错误——异常处理
手把手教你用go语言实现异常处理
1. 错误处理:当函数返回一个错误值时,需要对该错误进行处理。可以使用`if err != nil`语句来检查错误,并采取相应的处理逻辑。
查拉图斯特拉说
2024/01/11
5840
手把手教你用go语言实现异常处理
18.Go语言-错误与异常
在 Go 中, 错误 使用内建的 error 类型表示。error 类型是一个接口类型,它的定义如下:
面向加薪学习
2022/09/04
3750
Go错误处理机制: panic、recover与error处理
Go语言以其简洁明了的错误处理机制而著称。这一机制包括传统的error返回值、以及用于处理严重错误的panic和recover机制。本篇博客将深入浅出地介绍Go语言中的panic、recover与error处理,并通过实例代码帮助您理解如何正确地处理程序中的错误。
Jimaks
2024/12/07
3530
人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11
    人非圣贤,孰能无过,有则改之,无则加勉。在编程语言层面,错误处理方式大体上有两大流派,分别是以Python为代表的异常捕获机制(try....catch);以及以Go lang为代表的错误返回机制(return error),前者是自动化流程,模式化的语法隔离正常逻辑和错误逻辑,而后者,需要将错误处理判断编排在正常逻辑中。虽然模式化语法更容易让人理解,但从系统资源开销角度看,错误返回机制明显更具优势。
用户9127725
2022/09/22
1730
Go语言入门——进阶语法篇(四)
Go语言没有类似Java或Python那种try...catch...机制处理异常,Go的哲学是与众不同的,Go的设计者认为主流的异常处理机制是一种被过度滥用的技巧,而且存在很大的潜在危害,Go的异常处理(或者说是错误处理)是一种非常简单直观的方式。通常的,我们在写Java、Python之类的代码时,遇到可能存在的异常,直接用try括起来,使用catch捕获,然后就万事大吉了,当系统长时间的运行时,大大增加了不稳定性,所积累的问题可能在某一刻爆发。而Go者使用一种称为"恐慌的"机制,在有必要时,直接让系统宕机,让问题发生时立刻暴露出来,不必累积。很难说哪种设计更好,但Go语言确实简化了代码。
arcticfox
2019/09/03
5250
【愚公系列】2022年08月 Go教学课程 038-异常处理
异常处理,是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况,异常处理的好处是你不用再绞尽脑汁去考虑各种错误,这为处理某一类错误提供了一个很有效的方法,使编程效率大大提高。
愚公搬代码
2022/09/21
1620
【愚公系列】2022年08月 Go教学课程 038-异常处理
6.Go-错误,defer,panic和recover
  defer最常用的就是关闭连接(数据库,文件等),可以打开连接后紧跟defer进行关闭
zhang_derek
2019/08/12
4700
Go 函数的健壮性、panic异常处理、defer 机制
函数的使用者可能是任何人,这些人在使用函数之前可能都没有阅读过任何手册或文档,他们会向函数传入你意想不到的参数。因此,为了保证函数的健壮性,函数需要对所有输入的参数进行合法性的检查。一旦发现问题,立即终止函数的执行,返回预设的错误值。
贾维斯Echo
2023/10/23
5070
Go 函数的健壮性、panic异常处理、defer 机制
Go 语言错误及异常处理篇(三):panic 和 recover
前面学院君介绍了 Go 语言通过 error 接口统一进行错误处理,但这些错误都是我们在编写代码时就已经预见并返回的,对于某些运行时错误,比如数组越界、除数为0、空指针引用,这些 Go 语言是怎么处理的呢?
学院君
2019/08/19
1.5K0
Go 语言错误及异常处理篇(三):panic 和 recover
【Go 基础篇】Go语言中的defer和recover:优雅处理错误
Go语言以其简洁、高效和强大的特性受到了开发者的热烈欢迎。在错误处理方面,Go语言提供了一种优雅的机制,即通过defer和recover组合来处理恐慌(panic)错误。本文将详细介绍Go语言中的defer和recover机制,探讨其工作原理和在实际开发中的应用。
繁依Fanyi
2023/10/12
4210
【Go 基础篇】Go语言中的defer和recover:优雅处理错误
Go两周入门系列-错误处理
go语言中主要有两类错误,一类是可预见的错误,不会导致程序退出,一类是不可预见的错误,会导致程序退出。
用户10002156
2023/10/08
2050
Go两周入门系列-错误处理
【初识Go】| Day10 异常处理
针对这样的情况,Go语言中引入 error 接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含 error。error 处理过程类似于C语言中的错误码,可逐层返回,直到被处理。
yussuy
2020/12/23
2690
【初识Go】| Day10 异常处理
深入Go的错误处理机制(一)使用
程序运行过程中不可避免的发生各种错误,要想让自己的程序保持较高的健壮性,那么异常,错误处理是需要考虑周全的,每个编程语言提供了一套自己的异常错误处理机制,在Go中,你知道了吗?接下来我们一起看看Go的异常错误机制。
阿伟
2019/07/30
5970
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
大家好,我是猫头虎!今天我们继续探索Go语言的奥秘,迎来了我们的第23天学习之旅。在这一天,我们将重点关注Go语言中的错误处理机制。在实际的工程项目中,通过程序错误信息快速定位问题是我们的期望,但我们又不希望错误处理代码显得冗余和啰嗦。Go语言通过函数返回值逐层向上抛出错误,与Java和C#的try...catch异常处理显著不同。这种设计理念鼓励工程师显式地检查错误,以避免忽略应处理的错误,从而确保代码的健壮性。🚀
猫头虎
2024/04/09
1770
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
Go 专栏|错误处理:defer,panic 和 recover
最近校招又开始了,我也接到了一些面试工作,当我问「你觉得自己有什么优势」时,十个人里有八个的回答里会有一条「精力充沛,能加班」。
AlwaysBeta
2021/09/07
3650
Go 专栏|错误处理:defer,panic 和 recover
《快学 Go 语言》第 10 课 —— 错误与异常
Go 语言的异常处理语法绝对是独树一帜,在我见过的诸多高级语言中,Go 语言的错误处理形式就是一朵奇葩。一方面它鼓励你使用 C 语言的形式将错误通过返回值来进行传递,另一方面它还提供了高级语言一般都有的异常抛出和捕获的形式,但是又不鼓励你使用这个形式。后面我们统一将返回值形式的称为「错误」,将抛出捕获形式的称为「异常」。
老钱
2018/12/19
4480
Gin框架 - 自定义错误处理
很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上,地址:https://github.com/xinliangnote/Go
新亮
2019/07/29
1.8K0
推荐阅读
相关推荐
Go语言实战:错误处理和panic_recover之自定义错误类型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验