go错误处理核心是显式error+defer清理+panic/recover保底;defer须在资源获取后立即声明,recover仅在defer函数内有效且需命名返回值配合才能转panic为error。

Go 的错误处理不靠 try-catch,核心是显式 error + defer 清理 + panic/recover 保底;但滥用 panic 或写错 defer 位置,反而会让错误更难定位、资源更易泄漏。
defer 必须在资源获取后立即声明
常见错误是打开文件、获取锁、建立连接后,中间穿插大量逻辑才写 defer,一旦中间 panic,defer 还没注册,资源就漏了。
- 正确做法:获取资源后「立刻」写
defer resource.Close(),哪怕只隔一行也不安全 - 尤其注意
os.Open、sql.DB.Query、sync.Mutex.Lock等操作后必须紧跟defer - 多个资源按获取顺序写
defer,但执行是逆序——所以关闭顺序天然合理(比如先开 file 再开 writer,defer 写 writer.Flush() 在前、file.Close() 在后)
recover 只能在 defer 函数里调用才有效
recover() 不是监听器,它只在 panic 正在发生、栈还没完全展开的瞬间起作用。把它写在普通逻辑里,永远返回 nil。
- ❌ 错误写法:
if r := recover(); r != nil { ... }放在http.HandlerFunc主体里 - ✅ 正确写法:必须包裹在
defer func() { ... }()内部,且该defer必须在可能 panic 的代码之前注册 - 注意:recover 捕获的是当前 goroutine 的 panic;子 goroutine panic 不会触发主 goroutine 的 defer/recover
panic 值要可判断,别用裸字符串或 nil
用 panic("not found") 或 panic(nil) 后,recover() 返回值类型难断言,HTTP 中间件没法区分 404 和 500。
- 推荐自定义错误类型,比如
type HTTPError struct{ Code int; Msg string },然后panic(&HTTPError{Code: 404, Msg: "user not exist"}) - 或统一用带前缀的字符串,如
panic("404:user not found"),再在 recover 里用strings.HasPrefix(r, "404:")判断 - 避免
panic(123)、panic(struct{}{})—— 它们无法被有意义地分类处理
命名返回值 + defer 修改,才能从 panic 转 error
想让函数在 panic 后仍返回 error,光 recover 不够,还得靠命名返回值机制。
- 函数签名必须是命名返回,例如
func doWork() (data string, err error) - 在 defer 函数里检查
recover()结果,并显式赋值给err:if r := recover(); r != nil { err = fmt.Errorf("panic: %v", r) } - 注意:匿名返回值无法被 defer 修改;未命名的
return "ok", errors.New("x")也不会被 defer 中的赋值覆盖
最易被忽略的一点:defer 中修改命名返回值,只对「当前函数」生效;如果 panic 发生在下游函数,上层函数的 defer 并不能修改它的返回值——错误传播链必须手动透传,不能依赖 recover 自动“向上翻转”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











