Go 不支持 try/catch,必须显式检查 error;忽略 err 会导致 panic 或脏数据,应始终处理并包装错误、区分可恢复/不可恢复错误,避免滥用 panic 和字符串判断错误。

Go 语言不支持 try/catch,错误必须显式检查和传递;忽略 error 返回值是绝大多数线上 panic 或逻辑错乱的根源。
为什么不能忽略 err 返回值
Go 的函数常以 func() (T, error) 形式返回结果与错误,编译器不会强制你处理 error,但一旦跳过,就等于假设“这步绝不会失败”——而文件不存在、网络超时、JSON 解析失败、数据库连接中断,全都会默默变成 nil 值或零值,后续操作极易 panic 或写入脏数据。
常见错误现象:
- 调用
os.Open后直接对返回的*os.File调用Read,没检查err,结果file == nil导致 panic - 用
json.Unmarshal解析用户输入,忽略err,导致结构体字段保持零值却误判为“解析成功” - 在 HTTP handler 中调用业务函数后不检查
err,直接返回 200,实际数据根本没存进 DB
if err != nil 是起点,不是终点
裸写 if err != nil { return err } 适合内部函数,但对外暴露的接口或关键路径上,需补充上下文、日志、分类处理:
立即学习“go语言免费学习笔记(深入)”;
- 用
fmt.Errorf("failed to parse config: %w", err)包装错误(%w保留原始错误链),便于后期用errors.Is或errors.As判断类型 - 对可恢复错误(如临时网络抖动)考虑重试,而非直接返回;对不可恢复错误(如配置格式错误)应提前退出并记录清晰原因
- 避免在循环里反复
log.Printf同一类错误,改用计数 + 摘要日志,防止刷屏
示例:
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
data, err := ioutil.ReadFile("config.json")
if err != nil {
return fmt.Errorf("read config file failed: %w", err)
}
什么时候该用 panic?基本不该用
panic 在 Go 中仅适用于**程序无法继续运行的致命状态**,比如初始化阶段读不到必需配置、全局单例构造失败、断言本应成立但被破坏。它不是错误处理机制,而是崩溃信号。
- HTTP handler、RPC 方法、定时任务中禁止
panic—— 应统一用return err,由上层中间件/框架捕获并转为 500 或降级响应 -
recover仅应在极少数封装层(如 gin 的recovery()中间件)中使用,业务代码里写defer recover()是反模式 - 测试中可用
panic模拟异常路径,但生产代码中所有外部输入、IO、第三方调用都必须走error分支
自定义错误类型比字符串判断更可靠
用字符串匹配错误信息(如 strings.Contains(err.Error(), "timeout"))脆弱且难维护。应定义错误变量或实现 error 接口:
- 简单场景:用
var ErrTimeout = errors.New("request timeout"),再用errors.Is(err, ErrTimeout)判断 - 需携带额外信息(如重试次数、trace ID):定义结构体并实现
Error() string方法 - 注意:自定义错误类型不要嵌套过多层,否则
errors.Unwrap链过长会影响诊断效率
性能提示:fmt.Errorf("%w", err) 开销远小于拼接长字符串,且保留栈信息,推荐优先使用。
真正难的不是写 if err != nil,而是想清楚这个错误发生时,调用方需要知道什么、能做什么、要不要重试、是否要告警——错误处理的本质是接口契约的设计。









