error用于可预期、可恢复的业务错误,如文件打开失败、HTTP超时;panic仅用于不可恢复的编程错误或初始化致命失败,如配置缺失、违反不变量,且有性能开销。

error 用于可预期、可恢复的业务错误
当错误是调用方能预判、能处理、不影响程序整体运行时,必须用 error。比如打开文件失败、HTTP 请求超时、JSON 解析出错、用户输入参数校验不通过——这些都不是程序“坏了”,只是“这次没成功”。Go 要求你显式检查 if err != nil,而不是靠隐式跳转。
常见错误现象:把本该返回 error 的逻辑写成 panic,导致上层无法降级(如 fallback 到默认配置)、无法重试、无法记录警告日志后继续服务。
-
error是值,可传递、包装(fmt.Errorf("xxx: %w", err))、延迟处理 - 标准库几乎全部使用
error:例如os.Open、http.Get、json.Unmarshal - 性能无额外开销;不会触发堆栈展开;适合高频调用路径
panic 仅用于不可恢复的编程错误或初始化致命失败
panic 不是错误处理机制,而是“程序已无法安全继续”的信号。它不该出现在常规请求处理中,只应在以下两类场景出现:
- 程序启动阶段发现根本性缺陷:如关键配置缺失、数据库连接字符串为空、证书文件读取失败且无备用方案
- 检测到违反程序不变量的内部错误:如某个函数断言
len(s) > 0却收到空切片,而该条件是整个模块逻辑成立的前提
典型反例:在 HTTP handler 里对用户传入的 ID 做 strconv.Atoi 失败就 panic——这属于可预期输入错误,应返回 400 Bad Request + error。
立即学习“go语言免费学习笔记(深入)”;
注意:panic 触发后会执行所有已注册的 defer,但若未被 recover 捕获,goroutine 就终止,且无法保证其他 goroutine 安全退出。
recover 只能在 defer 函数中起作用,且不推荐在业务层滥用
recover 不是 Go 的 “catch 异常” 工具,它的唯一合理用途是:在顶层 goroutine(如 HTTP server 的 handler、CLI 命令入口)中兜底捕获意外 panic,记录堆栈、清理资源、返回友好错误,避免整个服务崩溃。
常见错误现象:在普通业务函数里写 defer func() { recover() }(),试图“吞掉”所有错误——这掩盖了本该修复的 bug,让问题更难定位。
-
recover()必须在defer函数内调用才有效,否则返回nil - 不要在中间层函数中
recover后再返回error,这混淆了错误语义:本该 panic 的严重问题被降级为普通 error,调用方无法区分轻重 - 标准库极少主动
recover,例如net/http的 handler panic 会导致 500,这是有意为之的设计
容易被忽略的底层事实:panic 有真实性能成本
每次 panic 都要构造完整调用栈、遍历 defer 链、做堆栈展开(stack unwinding)。这不是简单跳转,而是一次相对昂贵的运行时操作。
如果你在循环里、高频 API 中、或并发量大的 goroutine 里频繁触发 panic(哪怕被 recover 捕获),性能会明显劣化,且 GC 压力上升。
真正需要警惕的是:有些团队把 panic/recover 当作“快捷错误处理”,结果在压测时发现 P99 延迟飙升——这时回看代码,往往发现本该用 if err != nil 处理的 IO 错误,被写成了 panic。


















