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 更优雅。
相关推荐
纪念 2297 分钟前
C++ 算法(模拟)
开发语言·c++·算法
niucloud-admin15 分钟前
NIUCLOUD PHP SAAS上门家政使用文档--服务订单:评价列表
开发语言·php
Wang's Blog25 分钟前
Java 项目实战: 交友项目 - 项目:需求分析与滑动数据设计
java·开发语言·交友
gz927cool42 分钟前
Python 全栈应用最佳实践完整指南
开发语言·python·ai
CoderYanger1 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc
AIFQuant1 小时前
Python期货行情API接入:股指、黄金、原油多品种数据管道实战
开发语言·python·金融·区块链·量化开发
Thneonl1 小时前
Claude Code 频繁 Compacting?发布次日我根治了 2 个痛点
后端·rust
echome8881 小时前
JavaScript async/await 错误处理:别让 Promise 静默失败
开发语言·前端·javascript·promise
运维行者_1 小时前
供应链安全成最大风险源,企业如何构建可控的运维体系?
运维·开发语言·网络·数据库·安全·云计算·流量监控
.YM.Z1 小时前
优选算法——【双指针算法2】C++实现
开发语言·c++·算法