defer总是在函数真正退出前执行,无论return还是panic;recover只在defer函数体内直接调用才有效,且必须和panic在同一goroutine;defer按lifo顺序执行,参数在声明时求值,命名返回值可在defer中修改。

defer 总是在函数真正退出前执行,无论 return 还是 panic;recover 只在 defer 函数体内直接调用才有效,且必须和 panic 在同一个 goroutine。
defer 执行时机:在 return 赋值后、函数栈帧销毁前
Go 的 return 不是原子操作:它先计算返回值(写入命名返回变量或临时寄存器),再触发 defer,最后弹出栈帧。这意味着:
- 命名返回值(如
func f() (x int) { ... })是真实变量,defer中可修改其值,影响最终返回结果 - 非命名返回(如
func f() int { return 42 })的返回值在进入defer前已拷贝完成,defer改x没效果 - 多个
defer按后进先出(LIFO)顺序执行,比如defer A(); defer B(); defer C()实际执行顺序是 C→B→A
panic 触发时 defer 仍全部执行,但必须已注册
panic 不会跳过已注册的 defer,但也不会执行 panic 之后才遇到的 defer。常见错误包括:
- 把
defer func() { recover() }()写在panic("boom")后面 → 它根本不会被注册,recover永远不运行 - 在循环里写
defer close(ch)→ 所有defer累积到函数末尾才执行,可能造成 channel 关闭延迟甚至 panic - 在
init或包级变量初始化中使用defer→ 编译报错,defer只允许在函数内
recover 必须在 defer 中直接调用,且只捕获当前 goroutine 的 panic
recover() 是个“一次性开关”,只在两个条件下返回非 nil 值:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 它出现在
defer函数体内部(不能包一层普通函数再调用) - 当前 goroutine 正处于未被捕获的 panic 流程中(即 panic 已发生、尚未被其他 recover 拦截)
- 跨 goroutine 调用无效:比如
go func() { panic("x") }()后在主 goroutine 调recover(),一定返回nil
典型正确写法:defer func() { if r := recover(); r != nil { log.Println(r) } }()
参数求值发生在 defer 声明时,不是执行时
这个细节极易踩坑,尤其和闭包、变量复用混用时:
-
i := 1; defer fmt.Println(i); i = 2→ 输出1,不是2 for i := 0; i → 输出 <code>3三次,因为i是循环变量,defer 声明时没捕获副本- 修复方式:用局部变量绑定,如
for i := 0; i
真正容易被忽略的是:这个求值规则也适用于函数调用参数,比如 defer calc("1", a, calc("10", a, b)) 中,calc("10", a, b) 在 defer 声明时就执行了,不是 defer 实际运行时。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










