Go中error是值而非异常,必须紧邻调用后立即用if err != nil检查并响应;应优先用errors.Is/As判断,%w包装前须判空,固定错误用errors.New,动态内容才用fmt.Errorf。

if err != nil 必须紧跟调用之后
Go 不会跳过失败逻辑,也不会中断后续执行。漏掉检查,v可能为零值、nil指针,接着被解引用导致 panic;os.Open失败后继续读,实际在操作空文件句柄。
- 禁止写成
_, err := doSomething()然后只处理err——关键返回值丢了,错误还缺上下文 - 初始化阶段(如
main开头)可用log.Fatal,但 HTTP handler 或 goroutine 中必须return或显式降级,不能只打日志 - 循环里批量操作时,单个失败不能静默跳过:要
break、continue或收集错误聚合返回,否则“成功”假象会污染下游
fmt.Errorf("%w") 是包装错误的唯一安全方式
不用 %w,等于主动切断错误链。用 fmt.Errorf("load config: %v", err) 或字符串拼接,原始错误类型(比如 os.ErrNotExist)就没了,errors.Is(err, os.ErrNotExist) 永远返回 false。
- 正确写法:
return fmt.Errorf("loading config failed: %w", err) - 包装层级不宜超过 3 层——调试时容易迷失;优先在模块边界(如函数出口、HTTP handler 入口)包装一次,补业务语义即可
- 中间层不需要堆栈;真要诊断路径,只在最外层用
errors.AddStack(Go 1.22+)或第三方库
errors.Is 和 errors.As 不是万能判断工具
这两个函数不是为了“优雅地判断错误”,而是为了解耦错误构造与消费。它们只在跨包、跨层做分类时才真正有用;单层逻辑里硬套反而增加理解成本。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
errors.Is(err, fs.ErrNotExist):适合判断标准库定义的哨兵错误,如文件、网络 IO 场景 -
errors.As(err, &target):仅当你需要提取自定义错误结构体字段(比如type ValidationError struct { Field string; Msg string })时才用 - 禁止对任意 error 都套
errors.Is(err, errors.New("xxx"))——每次调用新建实例,Is必然失败 - 自定义错误类型必须实现
Unwrap() error方法,否则%w包装后无法被Is/As追踪
自定义错误类型要嵌入原始 error 字段
只存字符串字段(如 Msg string)等于把诊断线索全删了。业务错误需要携带结构化信息,又得保留底层 I/O 错误能力,就必须嵌入 Err error。
立即学习“go语言免费学习笔记(深入)”;
- 推荐结构:
type AppError struct { Op string; Code int; Err error },并在Error()方法中组合输出 - HTTP 服务中可据此映射状态码:
if errors.As(err, &validationErr) { http.Error(w, validationErr.Msg, http.StatusBadRequest) } - 不嵌入
Err字段,errors.As提取不到原始*os.PathError,errors.Is也判不了os.ErrNotExist - 哨兵错误(如
var ErrNotFound = errors.New("not found"))适用于常量级错误,可导出复用;运行时错误一律用fmt.Errorf+%w

















