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 更优雅。
相关推荐
.select.2 小时前
C++ 右值引用
开发语言·c++
2401_874732532 小时前
C++中的装饰器模式
开发语言·c++·算法
万粉变现经纪人2 小时前
如何解决 pip install shapely 报错 GEOS C 库未找到 问题
c语言·开发语言·python·pycharm·bug·pandas·pip
Gopher_HBo2 小时前
Go并发原子操作 waitGroup 对象池
后端
源远流长jerry2 小时前
RDMA 基本元素详解:从 WQE 到 QP 再到 CQ
linux·开发语言·网络·tcp/ip·架构·ip
共享家95272 小时前
单例模式( 饿汉式与懒汉式 )
开发语言·javascript·ecmascript
苦瓜小生2 小时前
【黑马点评学习笔记 | 实战篇 】| 10-用户签到+UV统计
笔记·后端·学习
_饭团2 小时前
C 语言内存函数全解析:从 memcpy 到 memcmp 的使用与模拟实现
c语言·开发语言·c++·学习·算法·面试·改行学it
~无忧花开~2 小时前
React组件与Props完全指南
开发语言·前端·react