Go语言的错误处理机制

Go语言的错误处理机制

recover

  • 每次defer都会将defer函数压入栈中,调用函数或者方法结束时,从栈中取出执行,所以多个defer的执行顺序是先入后出。

  • recover只能在defer函数中捕获异常,单独出现没有意义,发生异常的函数停止执行,其余函数继续执行。

  • defer、return、返回值三者的执行顺序是:

    1. 先给返回值赋值
    2. 执行defer语句
    3. 包裹函数return返回
    go 复制代码
    func f() (r int) {
         t := 5
         defer func() {
           r = r + 5  // 直接修改返回值变量r
         }()
         return t     // 第一步r=5 → defer执行r=10 → 返回10
    }
    // 等价于
    	func f() (r int) {
         t := 5	// 第1步
         r = t		// 第2步
         defer func() {
           r = r + 5  // 第3步
         }()
         return     // 第4步
    }
    // f()执行结果为t->5

代码示例

go 复制代码
package main

import (
	"fmt"
)

func test() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Recovered:", err)
		}
	}()
	a, b := 1, 0
	fmt.Println(a / b)
	fmt.Println("Error After")
}

func main() {
	fmt.Println("Start")
	test()
	fmt.Println("End")
}

执行结果如下:

powershell 复制代码
Start
Recovered: runtime error: integer divide by zero
End

代码解析

  1. fmt.Println("Start")程序开始;
  2. 进入函数test();执行到fmt.Println(a / b),除数不能为0,所以该错误会立即被recover捕获,进入到defer函数中,该处打印错误信息Recovered: runtime error: integer divide by zero;此时test()函数结束执行,fmt.Println(a / b)下方的fmt.Println("Error After")不会被执行;
  3. fmt.Println("End")程序结束;

多唠两句

recover并不能阻止错误发生,而是让程序「不崩溃」,并能处理错误后继续运行。

panic

  1. 它会立即终止当前 goroutine 的正常代码执行流程;
  2. 如果没有 recover 兜底,会导致整个程序崩溃退出;
  3. 可以理解为:程序运行中遇到了 "没法继续往前走" 的致命问题,只能 "喊停"。
  4. 包含两大核心场景:Go 语言自动触发、开发者手动触发

panic 的执行机制

  1. 立即终止当前行及后续的正常代码;
  2. 倒序执行当前函数中已声明的 defer 语句(后进先出);
  3. 向上传递到调用方函数,重复步骤 1-2(相当于 "逐层回退");
  4. 如果整个调用链中没有 recover,程序最终崩溃退出。

代码示例

go 复制代码
package main

import "fmt"

// 普通error:参数错误(可预期)
func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, fmt.Errorf("除数不能为0") // 返回error,不终止程序
	}
	return a / b, nil
}

// panic:空指针(不可恢复)
func nilPointer() {
	var p *int
	*p = 10 // 自动触发panic
}

func main() {
	// 处理error
	res, err := divide(10, 0)
	if err != nil {
		fmt.Println("处理error:", err) // 输出:处理error:除数不能为0
	} else {
		fmt.Println("结果:", res)
	}

	// 处理panic
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("处理panic:", err) // 输出:处理panic: runtime error: invalid memory address or nil pointer dereference
		}
	}()
	nilPointer()

	// 处理完panic后,程序继续运行
	fmt.Println("程序未崩溃,正常结束")
}

执行结果如下:

powershell 复制代码
处理error: 除数不能为0
处理panic: runtime error: invalid memory address or nil pointer dereference
程序未崩溃,正常结束

多唠两句

  • 尽量少用手动 panic:业务逻辑优先用 error 处理,panic 只留给 "程序没法运行" 的极端场景;
  • recover 只在顶层函数兜底:比如 HTTP 服务的中间件、gRPC 拦截器、main 函数,避免在底层函数滥用 recover(会隐藏问题);
  • recover 后记录日志:捕获 panic 时,一定要记录完整的堆栈信息(用 debug.Stack()),方便排查问题;
  • 不要用 panic 替代 error:比如用户输入错误、接口调用失败,用 error 更优雅。
相关推荐
考虑考虑1 小时前
cmd局部设置java变量
运维·后端·自动化运维
2601_966949651 小时前
为什么量化策略需要大量历史股票数据?从回测可信度理解数据规模
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
niucloud-admin1 小时前
JAVA V6 多商户商城 开发文档——插件目录结构
java·开发语言
滕州市燕猫虎计算机科技工作室个体工商户2 小时前
Java锁
java·开发语言
多加点辣也没关系2 小时前
JavaScript|第31章:表单与控件
开发语言·javascript·ecmascript
菜鸟~noob2332 小时前
【电子战】 第09篇:阵列信号处理——波束形成、MVDR、MUSIC【含matlab代码】
开发语言·matlab·信号处理
ocean21032 小时前
2025-2026年Python面试高频知识点洞察
开发语言·python·面试·python八股文
零依赖极客3 小时前
Day 6·1 ARMv8.2 dotprod——vdotq_s32 一条指令做 4 个点积
c语言·开发语言·人工智能·矩阵·arm·vllm
陈随易4 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
新时代牛马4 小时前
cyclictest 毛刺从哪来?从ftrace、latencytop、perf到IRQ/调度延迟定位
android·开发语言·python·kotlin