error 与 panic 各自适合什么场景?
结论先行
一句话区分:error 用于“可以预期、应交给调用方决定如何处理”的失败(文件不存在、网络超时),失败作为普通返回值逐层传播;panic 用于“不该发生、已破坏不变量”的编程错误或不可恢复状态,触发后沿调用栈展开,必要时由 defer+recover 兜底。
要点
- error 是内置接口:type error interface { Error() string },几乎所有可能失败的函数都应返回它。
- error 可组合:fmt.Errorf 用 %w 包裹形成错误链,errors.Is/As 负责沿链判等与类型断言。
- panic 语义:立即停止当前函数,沿途执行 defer 并展开栈,直到被 recover 捕获或程序崩溃退出。
- 设计原则:库代码优先返回 error,调用方才知道“失败了该怎么继续”。
- panic 的正当用途:前提条件被破坏(如 index out of range 属于语言自带 panic)、不可达分支、程序无法继续运行的致命初始化错误(配合 fail fast)。
- 不要把 panic 当异常体系用:Go 没有 try/catch,业务失败走 error 才能在调用链中被优雅处理。
- recover 只放在边界:HTTP handler、goroutine 入口等“隔离点”,防止单点 panic 拖垮整个进程。
示例
var ErrNotFound = errors.New("not found")
func Find(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("find %d: %w", id, ErrNotFound)
}
return "ok", nil
}
_, err := Find(-1)
if errors.Is(err, ErrNotFound) { // 沿错误链匹配
fmt.Println("handle not found")
}
常见追问/记忆点
- 追问:panic 会绕过 defer 吗?答:不会,展开过程会按 LIFO 执行沿途 defer,recover 可在其中截断。
- 追问:标准库什么时候 panic?答:编程错误居多(下标越界、类型断言失败、并发写 map),业务失败一律返回 error。
- 追问:error 里能带堆栈吗?答:标准库不自动带;可自行包装或用第三方 error 库附加栈信息。
- 记忆:预期失败用 error,不变量破坏用 panic;recover 只该出现在隔离边界。