defer不处理错误,只保证函数退出时执行;它注册延迟调用,按后进先出执行,错误仍需显式判断或recover配合;命名返回值下defer可修改err,但需注意引用时机。

defer 本身不处理错误,它只保证函数退出时执行
很多人误以为 defer 能“捕获”或“传递”错误,其实它只是注册一个延迟调用——无论函数是正常返回还是 panic,只要执行到 defer 语句,该调用就会被压入栈,等函数真正返回时才按后进先出顺序执行。错误处理仍需靠显式判断 err 值或配合 recover。
defer + named return 实现统一错误清理
当函数有命名返回值(如 func foo() (err error)),defer 中的匿名函数可读写该变量,从而在资源释放后“补救”错误值。但要注意:defer 闭包捕获的是变量的引用,不是快照。
- 常见错误:在 defer 中直接打印
err却没看到预期值——因为此时err还未被主函数体赋值,仍是零值 - 正确做法:用带参数的匿名函数显式传入当前 err,或依赖命名返回值机制
- 示例:
func writeToFile(name string) (err error) { f, err := os.Create(name) if err != nil { return } defer func() { if closeErr := f.Close(); closeErr != nil && err == nil { err = closeErr } }() _, err = f.Write([]byte("hello")) return }
defer 不适合替代 if err != nil { return } 的错误检查
延迟执行无法改变控制流,不能提前退出函数。把所有错误检查堆在最后再统一处理,会导致资源泄漏、逻辑混乱。
- 打开文件后不立即检查
err,而是 defer 关闭,接着继续操作——一旦os.Create失败,f是 nil,后续f.Write会 panic - 数据库事务中,不能靠 defer 回滚:必须在
Begin后立刻检查 err,失败就跳过 defer 的Rollback注册 - 性能影响:无条件注册 defer 会带来微小开销(函数调用栈管理),高频路径应避免无关 defer
panic/recover 场景下 defer 是唯一能运行的“兜底”机会
只有在这个场景中,defer 才真正承担起“错误处理”的角色——它是在 panic 发生后、程序崩溃前,唯一还能执行用户代码的地方。
立即学习“go语言免费学习笔记(深入)”;
- 必须在 panic 发生前注册 defer,且 recover 必须在 defer 函数中调用才有效
- recover 只对同一 goroutine 中的 panic 有效,且只能恢复一次
- 不要滥用:HTTP handler 中 recover 可防止整个服务崩溃,但掩盖了本该暴露的编程错误(如空指针)
- 示例:
func safeHandler() { defer func() { if r := recover(); r != nil { log.Printf("recovered from panic: %v", r) } }() panic("something went wrong") }










