不必要,但对关键、可重用、需判断的错误必须定义;仅当错误被多函数返回、需errors.Is/==判断、含义稳定且属公开契约时,才定义包级var错误变量。
var 变量?不必要,但对关键、可重用、需判断的错误必须定义。Go 的错误处理鼓励“按需定义”,不是“按数量定义”。盲目为每个 fmt.Errorf 或 errors.New 都配一个包级变量,反而会增加维护成本和语义模糊性。
var?只在满足以下全部条件时才定义包级错误变量:
io.EOF)errors.Is 或 == 显式判断(如重试逻辑、状态分流)fmt
.Errorf("user %s not found", id) 不适合定义为变量)ErrAlreadyClosed),而非内部临时错误推荐使用 var + errors.New,避免用 fmt.Errorf 初始化变量(后者可能隐含格式化开销且不可比较):
var (
ErrInvalidID = errors.New("invalid user ID")
ErrNotFound = errors.New("resource not found")
ErrAlreadyExists = errors.New("resource already exists")
)
注意:
fmt.Errorf("invalid ID: %s", id) 赋值给包级 var —— 这会让变量失去恒定值,errors.Is(err, ErrInvalidID) 永远失败fmt.Errorf("%w", err))存为全局变量 —— 它们本应是运行时构造的fmt.Errorf 更合适?绝大多数业务逻辑中的非关键、一次性、带动态信息的错误,直接内联更清晰:
if len(name) == 0 {
return fmt.Errorf("CreateUser: name cannot be empty")
}
if !isValidEmail(email) {
return fmt.Errorf("CreateUser: invalid email format: %q", email)
}
这类错误通常只被日志记录或透传给上层,无需被下游代码 Is 判断,也不构成 API 稳定性承诺。
过度提取会导致包里堆满 ErrXXX 却没人真去判断;反过来,该提取时不提取,又会让错误处理散落在各处、无法统一拦截。边界就在“是否会被 errors.Is 查”和“是否代表一个稳定语义”。