panic仅用于真正不可恢复的程序崩溃场景,如初始化失败、空指针解引用、严重数据不一致;绝不应用于http handler业务错误或流程控制,应优先显式处理error。

Go 里没有异常(exception),只有错误(error)——所有“异常处理”本质是显式判断和传递 error 值,不 catch、不 throw、不中断控制流。
什么时候该用 panic?
仅用于**真正不可恢复的程序崩溃场景**,比如:初始化失败(配置加载失败且无法降级)、空指针解引用(本不该发生)、严重数据不一致(校验发现内存结构已损坏)。
- Web HTTP handler 中绝不要用
panic处理业务错误(如参数校验失败、DB 查询为空),它会触发全局recover或直接终止 goroutine,掩盖真实问题 - 测试中可合理使用
panic模拟边界情况,但需配合defer recover()显式捕获 - 标准库如
json.Unmarshal遇到语法错误返回error,而非panic;只有sync.Mutex.Unlock在未加锁时调用才panic—— 这是 API 合约破坏,属于真正异常
error 值怎么构造才算“优雅”?
避免裸字符串 errors.New("failed to open file"),优先用 fmt.Errorf 包裹上下文,并带上关键变量值便于定位。
- 带栈信息?用
github.com/pkg/errors或 Go 1.13+ 的%w+errors.Is/errors.As:err := fmt.Errorf("failed to process order %d: %w", orderID, dbErr) - 不要在每层都重复包装,只在**语义变化处**加一层(例如:“数据库超时” → “下单失败”),否则栈信息冗余、
errors.Is判断失效 - 自定义错误类型仅当需要行为区分时才定义(如实现
Timeout() bool方法),普通业务错误用包装就够了
HTTP handler 里错误怎么透出给前端?
不能直接把底层 error(尤其是含敏感路径、SQL 片段的)返回给客户端,必须做转换和分级。
- 区分三类错误:
– 用户输入错误(400 Bad Request)→ 返回明确提示,如
{"code": "INVALID_PARAM", "message": "email format invalid"}– 系统临时故障(503 Service Unavailable)→ 不暴露细节,统一兜底文案 – 开发者需介入的错误(500 Internal Error)→ 记录完整error栈到日志,响应体只返回泛化码(如"UNKNOWN_ERROR") - 用中间件统一拦截 handler 返回的
error,避免每个 handler 写重复的if err != nil { ... } - 注意:Go 的
http.Error默认写入文本,务必手动设置Content-Type: application/json并序列化结构体,否则前端解析失败
为什么 if err != nil 看着丑却不能省?
这不是风格问题,是 Go 类型系统强制你直面错误分支——漏判 error 是生产环境最常见 panic 来源(比如 io.ReadFull 返回 io.ErrUnexpectedEOF 但没检查,后续直接 panic)。
- 工具链依赖这个模式:
go vet能检测未使用的error变量,staticcheck可查漏掉的错误检查 - 函数返回多个
error?说明职责过重,拆分。一个函数只该关心一种失败维度 - 真要简化写法?可用
errors.Join合并多个错误,或封装MustXXX函数(仅限 CLI 工具等可接受崩溃的场景)
最容易被忽略的是错误的**语义一致性**:同一错误码在不同接口中含义是否相同?日志里打印的 error 是否包含足够上下文又不含敏感信息?这些不靠语法规范,靠团队对每个错误路径的手动 review。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











