Go将错误视为需显式检查的返回值而非异常,强制通过if err != nil处理以避免无效状态;errors.New用于哨兵错误,fmt.Errorf配合%w包装保留错误链;panic/recover仅适用于不可恢复崩溃,业务错误须用error传播。

error 不是异常,也不是需要“捕获”的东西——它是函数正常返回的一部分,必须显式检查。Go 的错误处理机制倒逼你重新理解「控制流」:没有隐式跳转,没有栈展开,所有失败路径都写在明面上。
为什么 if err != nil 不能省略
这不是语法强制,而是语义必需。Go 把错误当作值来传递,不是运行时中断。一旦跳过 if err != nil,后续代码就可能基于无效状态运行(比如 nil 指针、空切片、未初始化的结构体字段)。
- 常见错误现象:
panic: runtime error: invalid memory address or nil pointer dereference,往往就发生在漏检err后直接用了返回的nil值 - 使用场景:几乎所有 I/O 操作(
os.Open、http.Get)、解析操作(json.Unmarshal)、校验逻辑(url.Parse)都遵循此模式 - 性能影响:无额外开销——不抛异常,不建栈帧,
error就是个接口值,和int或string一样轻量
errors.New 和 fmt.Errorf 的实际分工
二者都构造 error,但用途不同:前者用于固定消息的哨兵错误,后者用于带上下文的动态错误。
-
errors.New("file not found")适合定义包级常量(如var ErrNotFound = errors.New("not found")),方便用errors.Is(err, ErrNotFound)判断 -
fmt.Errorf("failed to parse %s: %w", filename, err)中的%w是关键——它包装错误并保留原始链,便于用errors.Unwrap或errors.Is追溯根因 - 容易踩的坑:用
fmt.Sprintf拼接错误字符串会切断错误链,丢失底层错误类型和信息
别用 recover 处理业务错误
panic/recover 是为真正意外的、无法恢复的崩溃准备的(如空指针解引用、越界访问),不是替代 error 的方案。
- 常见错误现象:把文件打开失败、网络超时、JSON 解析错误都塞进
panic,结果recover后程序状态不可知,资源(如未关闭的文件句柄)可能泄漏 - 正确做法:业务错误走
error返回;仅在顶层(如 HTTP handler)用defer+recover防止 panic 波及整个服务 - 兼容性影响:滥用
panic会让调用方无法做重试、降级、日志分级等可控响应
从「try-catch 思维」切换到「错误传播思维」
你不需要在每一层都“处理”错误,而要决定:这个错误该由谁负责?是立刻终止、重试、降级,还是原样向上交?
立即学习“go语言免费学习笔记(深入)”;
- 典型模式:
func A() error { if err := B(); err != nil { return fmt.Errorf("A failed: %w", err) } }—— 错误被增强上下文后继续上抛 - 容易忽略的点:错误包装不是越多越好。
fmt.Errorf("step1: %w") → fmt.Errorf("step2: %w")会导致日志里堆满重复前缀,应只在边界处(如 API 入口、RPC 调用点)添加有意义的上下文 - 真实约束:Go 1.13+ 的
errors.Is/errors.As让你能穿透多层包装判断错误本质,这要求你从一开始就不丢弃原始错误


















