Go语言错误是返回值而非异常,需显式检查、包装(%w)、传递或处理;标准库error在末位;用errors.Is/As判断类型;goroutine错误须通过channel等传出。

Go 语言中没有“捕获错误”这个动作,函数里不 catch、不 try、不 throw——错误是返回值,不是异常流。你真正要做的,是检查它、包装它、传递它,或根据上下文决定是否终止流程。
error 必须显式检查,不能忽略
几乎所有标准库和规范函数都把 error 作为最后一个返回值。不检查 err != nil 就继续用前面的返回值,大概率导致 panic 或逻辑错乱。
- 常见错误现象:读文件后没检查
os.Open的err,直接对nil文件句柄调用Read→panic: runtime error: invalid memory address - 正确做法:每次调用后立刻判断,且优先处理错误分支(避免嵌套过深)
- 不要写
if err == nil { /* 正常逻辑 */ },而应写if err != nil { /* 错误处理 */ return },让正常路径保持扁平
用 errors.Is 和 errors.As 判断错误类型
只靠 err != nil 不够,很多场景需要区分错误原因:文件不存在?权限不足?网络超时?这时不能用字符串匹配,要用标准方式识别。
-
os.IsNotExist(err)是快捷方式,本质是调用errors.Is(err, fs.ErrNotExist) - 自定义哨兵错误(如
var ErrNotFound = errors.New("not found"))必须用errors.Is(err, ErrNotFound)判断,而不是err == ErrNotFound(包装后地址不同) - 需要提取底层错误详情(比如带错误码的结构体),用
errors.As(err, &myErr)解包,myErr必须是指针
用 fmt.Errorf("%w") 包装错误并保留调用链
从 Go 1.13 起,%w 是包装错误的唯一推荐方式。它让错误形成可遍历的链,支持 errors.Is 和 errors.As 向下穿透。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 错误示例:
return fmt.Errorf("failed to parse config: %v", err)—— 丢失原始错误,无法用errors.Is判断 - 正确写法:
return fmt.Errorf("failed to parse config: %w", err)—— 原始错误被包裹,仍可被识别 - 注意:
%w只接受一个error类型参数,不能接字符串或多个错误;多次包装会形成链,但别无谓嵌套(比如连续三层%w)
goroutine 里的错误不能“捕获”,得传出来
在 goroutine 中发生错误,recover 无效(除非你真 panic 了),也不能靠 defer 捕获主 goroutine 的错误。唯一可靠方式是通过 channel 或回调传出。
- 典型结构:声明
errCh := make(chan error, 1),goroutine 执行完或出错时写入errCh - 主 goroutine 用
select或直接接收,配合context控制超时,避免死等 - 切忌在 goroutine 里直接
log.Fatal或os.Exit—— 会杀掉整个进程,不是局部错误处理 - 如果 goroutine 内部用了
panic,必须在它的 defer 里调用recover(),且只能捕获自己 goroutine 的 panic
最容易被忽略的一点:错误不是日志。同一错误在调用链上被多层 log.Printf("err: %v", err) 打印,既污染日志又掩盖源头。应该只在最外层(如 HTTP handler 或 CLI 入口)统一记录,并确保错误链完整、上下文清晰。

















