go错误处理(2)——panic函数使用及捕获

文章介绍了Go语言中使用panic和recover进行异常处理的方式。当遇到无法处理的错误时,可以调用panic终止程序并记录错误信息;而recover则可以在defer语句中捕获panic,防止程序崩溃,允许程序恢复执行。文中通过实例展示了如何在divide函数中使用这两个函数来处理除零错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前面我们讲过了error类型来处理一般的错误,本文会描述使用panic函数和recover函数来处理比较极端的错误。简单来说,当程序运行时遇到无法处理的错误或异常情况时,会调用panic函数引发一个运行时错误,此时程序会终止执行并抛出错误信息。为了避免程序崩溃,可以使用recover函数来捕获错误,并进行处理或恢复程序的执行,通常recover函数写在defer语句中。

panic函数

panic函数是内置的一个函数,他的结构如下:

func panic(v interface{})

此函数接受一个接口类型的值,这个值通常是错误的描述,调用次函数后,会引发异常,这将中止当前的程序流程,并在堆栈中记录当前的异常信息。此函数可以被主动调用,也可以在发生数组越界、空指针引用等情况下被动调用。

我们将上篇文章的除法例子进行小小的修改,使用panic进行错误处理:

package main

import (
   "fmt"
)

func divide(a, b int) int{
   if b == 0 {
      panic("division by zero")
   }
   return a / b
}

func main() {
   result := divide(10, 0)
   fmt.Println("the result is ",result)
}

运行后,程序直接退出了,报division by zero错误。

recover函数

有些时候,我们不希望panic函数直接粗暴的退出,这时候我们可以使用recover函数对pannic进行捕获,注意,recover函数只能写在defer关键字之后哦!

recover也是内置函数,他的结构如下:

func recover() interface{}

他能捕获到的panic函数报出的错误,并使用接口类型进行返回。

我们使用recover对例子再次修改,让程序可以继续的运行,而不是粗暴的退出,代码如下:

package main

import (
   "fmt"
)

func divide(a, b int) int{
   defer func() {
      if err:=recover();err!=nil {
         fmt.Printf("runtime panic : %v\n", err)
      }
   }()
   if b == 0 {
      panic("division by zero")
   }
   return a / b
}

func main() {
   result := divide(10, 0)
   fmt.Println("the result is ",result)
}

总结

本篇博客探讨了如何使用 panic 和 recover 来处理 Go 语言中的异常。当程序遇到无法处理的错误时,它可以使用 panic 函数引发异常,并使用 recover 函数结合defer关键词从异常中恢复。

### Go语言中 `defer` 捕获异常的用法 在Go语言中,`defer` 结合 `recover()` 是一种常见的捕获和处理运行时异常(即 `panic`)的方法。通过这种方式可以有效地将不可控的 `panic` 转换为可控的错误返回值。 #### 基本原理 当发生 `panic` 时,程序会立即停止当前函数的执行,并依次调用之前注册的所有 `defer` 函数。如果在某个 `defer` 中调用了 `recover()` 并成功恢复,则程序不会继续传播该 `panic`,而是按照正常的控制流继续执行后续代码[^2]。 以下是具体实现的一个典型例子: ```go package main import ( "fmt" ) func mayPanic() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() panic("An unexpected error occurred!") } func main() { mayPanic() fmt.Println("Continuing execution after recovery.") } ``` 在这个示例中,`mayPanic` 函数内部发生了 `panic`,但由于存在一个带有 `recover()` 的 `defer`,因此能够捕捉到这次 `panic` 并打印相应的消息。最终,程序得以继续正常运行而不是崩溃退出[^3]。 #### 将 `panic` 转换为 `error` 返回 除了简单地恢复程序外,还可以进一步增强功能——把特定类型的 `panic` 转变为标准库支持的标准错误形式 (`error`) 来传递给调用方。下面展示了一个这样的应用场景实例: ```go package main import ( "bytes" "errors" "io" ) // 自定义错误类型模拟某种特殊条件下的 Panic 场景 var ErrTooLarge = errors.New("buffer too large") func readData(reader io.Reader, size int) ([]byte, error) { buf := make([]byte, size) defer func() { if p := recover(); p != nil { switch v := p.(type) { case string: err := fmt.Errorf(v) panic(err) // 继续向上层抛出已封装好的 Error 类型对象而非原始字符串 case error: panic(v) // 已经是合法的 Error 实现无需额外包装直接再抛即可 default: panic(p) // 非预期的情况维持原样重新引发 Panic } } }() n, err := reader.Read(buf[:]) if n > len(buf)/2 { // 故意制造溢出状况以便测试我们的机制有效性 panic(ErrTooLarge) } else if err != nil && err != io.EOF{ return nil,err } return buf[:n],nil } func safeRead(reader io.Reader, maxSize int)(data []byte,err error){ data,err=readData(reader,maxSize) if e:=recover();e!=nil{ // 外部再次包裹一层 Defer Recover 构造确保任何未被捕获的异常都能被妥善处置 if er,ok:=e.(error);ok{ err=er }else{ panic(e)// 不属于预设范围内的异常则保持原有行为不做修改 } } return data,err } func main(){ var sampleReader=bytes.NewReader([]byte{'G','o'}) content,err:=safeRead(sampleReader,8) if err==ErrTooLarge{ fmt.Printf("Error:%v\n",err) }else{ fmt.Printf("Content Read Successfully:%s\n",string(content)) } } ``` 此代码片段展示了如何在一个更复杂的上下文中应用 `defer` 和 `recover()` 技术来管理潜在危险的操作,同时提供清晰明了的结果反馈路径给使用者[^4]。 ### 注意事项 虽然这种方法非常有用,但也需要注意过度依赖可能会掩盖真正的逻辑缺陷或者隐藏难以调试的问题所在位置;另外频繁使用也可能影响性能表现因为每次进入新的作用域都需要创建额外的闭包开销。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨雨不怕雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值