defer 参数在 defer 语句执行时即求值,函数体延迟至 return 前运行;x、time.now()、f() 等均在此刻求值并固定,后续修改无效;命名返回值可被 defer 修改,最终返回修改后值。

defer 的参数在 defer 语句执行那一刻就求值完毕,函数体里的代码则等到 return 前才运行——这不是 bug,是明确设计。
defer fmt.Println(x) 中的 x 是什么时候读的?
就在 defer 那一行被执行时,x 的当前值就被固定了,后续对 x 的修改完全不影响它。
- 写
defer fmt.Println(x)后再改x = 42,输出仍是旧值 - 写
defer fmt.Println(time.Now().Unix()),打印的是注册 defer 的时间戳,不是函数退出时的时间 - 写
defer close(f()),f()会立刻调用,不是等 return 时才调;如果f()返回一个需关闭的资源,这很可能导致 panic
为什么 defer func(){ t.q = t.q }() 恢复不了原值?
因为 t.q = t.q 是函数体内的赋值语句,右侧的 t.q 不是参数,不会被“快照”——它要等到 defer 真正执行时才读,而那时 t.q 早已被改过。
- 错误写法:
defer func() { t.q = t.q }()→ 实际执行的是t.q = 新值 - 正确写法一(参数捕获):
defer func(q int) { t.q = q }(t.q)→t.q在括号里就被读了一次 - 正确写法二(局部变量):
qOrig := t.q; defer func() { t.q = qOrig }() - 注意:不要在循环里直接写
for i := range s { defer f(i) },所有 defer 共享同一个i地址;应写成for i := range s { i := i; defer f(i) }
命名返回值在 defer 里能被修改吗?
能,但要看怎么改——return 语句本身会先给命名返回值赋值,然后才轮到 defer 执行。所以 defer 函数可以读、也可以改这个变量,最终返回的是 defer 修改后的值。
- 写
func f() (x int) { defer func() { x = 99 }(); return 123 },结果是99,不是123 - 但
defer fmt.Println(x)打印的是123(因为该 defer 注册在return之前,此时x还是 0) - 关键点:命名返回值是真实变量,
return 123等价于x = 123,不是跳过变量的原子操作
最易被忽略的其实是「参数求值」和「函数执行」这两个阶段的分离——很多人以为 defer 是整体延迟,其实只有函数体延迟,参数早被锁死了。写资源清理逻辑时,不提前捕获状态快照,很容易让 defer 变成无效操作。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











