go 文件操作必须显式检查 error,os.open 失败返回 nil 文件指针,直接调用 read 会 panic;defer close() 不报告错误,需手动捕获并合并;错误判断要用 errors.is,包装须用 %w。

Go 语言没有“捕获异常”这回事——panic 不是异常,error 也不是异常;文件操作失败时返回的是 error 值,必须手动检查,而不是“捕获”。
os.Open 返回 nil 文件指针 + error 时直接调用 Read 会 panic
这是最常踩的坑:误以为 os.Open 失败只是返回错误,其实它同时返回 nil 的 *os.File。后续若忽略 err 就调用 file.Read(),会立即触发 panic:
panic: runtime error: invalid memory address or nil pointer dereference
- 永远先判断
err != nil,再使用file - 不要写
file, _ := os.Open(...)—— 下划线丢弃错误等于埋雷 - 即使只读小文件,也别跳过检查;
os.ReadFile虽省事,但它的err同样要检查
defer file.Close() 不会自动报告 Close 错误
defer file.Close() 只保证执行,不处理它的返回值。而 Close() 可能失败(比如写缓存时磁盘满),这个错误会被静默丢弃。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 写文件时尤其危险:
io.WriteString成功 ≠ 数据已落盘,Close()才是最终校验点 - 正确做法是用匿名函数包裹
Close()并检查其错误,且只在主错误为nil时才覆盖它 - 示例逻辑:
defer func() { if closeErr := file.Close(); closeErr != nil && err == nil { err = fmt.Errorf("close file: %w", closeErr) } }()
区分文件不存在、权限不足等具体错误类型
不能只打印 err.Error() 就完事。业务逻辑往往需要差异化响应,比如文件不存在就用默认配置,权限不足则提示用户改 chmod。
- 用
os.IsNotExist(err)判断是否因路径不存在失败 - 用
os.IsPermission(err)区分权限问题 - 用
errors.Is(err, os.ErrNotExist)(推荐)替代字符串匹配,兼容包装后的错误 - 避免写
strings.Contains(err.Error(), "no such file")—— 不健壮、易断
错误包装要用 %w,否则链式判断失效
向上层传递错误时,如果只用 fmt.Errorf("xxx: %v", err),原始错误就被吞掉了,errors.Is 和 errors.As 都无法穿透。
- 必须用
%w动词:fmt.Errorf("load config: %w", err) - 这样调用方才能用
errors.Is(err, os.ErrNotExist)准确识别底层原因 - 日志中打印完整错误链用
fmt.Printf("%+v", err)(需github.com/pkg/errors或 Go 1.13+ 原生支持)
真正容易被忽略的,是 Close() 错误的处理时机和方式——它不在主流程里,却可能决定一次写操作是否真正成功;还有就是把 error 当成可选副产品,而不是控制流的必需分支。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










