go 中 error 是值而非异常,必须立即检查 err != nil,否则程序继续执行导致 panic 或状态污染;应紧随调用后处理,用 errors.is/as 判断错误类型,fmt.errorf 优先用 %w 包装,http handler 中避免 panic 或静默吞错。

Go 语言里没有异常,error 是值,不是控制流;不显式检查 err != nil,它就一直留着,程序照常往下跑——直到你用 nil 指针 panic 或逻辑错乱。
if err != nil 必须紧跟调用后一行
因为 error 是返回值,不是自动抛出的信号。标准库函数(如 os.Open、json.Unmarshal)都把 error 放在最后一个返回位,你不立刻检查,后续语句可能:
- 覆盖
err变量(比如下一行又赋值err := xxx()) - 对
nil的*os.File调用Read,触发panic: nil pointer dereference - 继续执行依赖资源已失败的逻辑,状态污染难以回溯
正确写法是紧贴调用写判断,哪怕只是 return err:
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
defer f.Close()
别用 if err != nil { log.Println(err); continue } 这类“吞掉错误还继续”的写法——它让失败静默蔓延。
errors.New 和 fmt.Errorf 选哪个
看错误信息是否含变量:
- 固定字符串(如
"invalid ID"、"connection refused")→ 用errors.New:轻量、无格式开销、支持errors.Is精确比对 - 带变量(如路径、ID、时间戳)→ 必须用
fmt.Errorf,且优先用%w包装底层错误:fmt.Errorf("parse %q: %w", input, err) - 误用
%s或%v替代%w(如fmt.Errorf("read: %s", err))会切断错误链,导致errors.Is和errors.As失效
错误消息不是日志,也不是给终端用户看的提示,而是给开发者 debug 用的上下文快照——别塞敏感路径或原始堆栈。
判断特定错误要用 errors.Is / errors.As,别用 == 或字符串匹配
底层错误常被多层包装(比如 fmt.Errorf("load config: %w", err)),直接比较会失效:
- ❌ 错误:
err == os.ErrNotExist或strings.Contains(err.Error(), "no such file") - ✅ 正确:
os.IsNotExist(err)(本质是调用errors.Is(err, os.ErrNotExist)) - 提取结构体字段(如
*os.PathError)用errors.As:var pe *os.PathError; if errors.As(err, &pe) { log.Println(pe.Path) } - 自定义错误要支持
errors.Is,必须实现Unwrap() error方法,返回嵌套的底层错误
errors.As 要求传入指针变量,且该变量需在 if 外声明,否则作用域一结束就丢弃了。
HTTP handler 里别 panic,也别静默吞 error
Web 请求中的 I/O 失败(数据库超时、文件不存在、JSON 解析失败)全是常态,不是 bug:
- ❌ 别
panic(err):整个 goroutine 崩溃,连接中断,监控难定位 - ❌ 别
if err != nil { log.Printf("ignored: %v", err); return }:前端收不到响应,请求卡死或超时 - ✅ 正确:按错误性质转成 HTTP 状态码 + 简洁响应体,例如:
http.Error(w, "invalid json", http.StatusBadRequest) - 中间件可统一处理未捕获的
error,但前提是业务层已明确区分「可恢复」和「需上报」的错误路径
真正该用 panic 的地方极少:全局配置解析失败且无默认值、init() 中发现程序根本无法启动的约束破坏——它不是错误处理机制,是“停机检修”信号。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











