4、错误码常量定义:使用iota定义错误码、错误码分组、错误码命名规范

在Go语言中,错误码常量的定义是领域边界断言的第一道防线。使用iota枚举器可以保证错误码的连续性与可维护性,同时通过分组与命名规范,让错误码具备自描述能力,降低跨模块的认知成本。

4.1 使用iota定义基础错误码

iota是Go预定义的标识符,在const块中每出现一次即自增1。利用这一特性,可以高效定义一组递增的错误码常量:

package errcode

type ErrCode int

const (
    ErrSuccess       ErrCode = iota // 0
    ErrUnknown                      // 1
    ErrInvalidParam                 // 2
    ErrNotFound                     // 3
    ErrTimeout                      // 4
    ErrPermissionDenied             // 5
)
关键点:使用iota定义错误码时,第一个常量必须显式赋值为iota(或0),后续常量自动递增。建议始终从0开始,将ErrSuccess作为零值,符合Go零值语义。

4.2 错误码分组:按领域边界划分

单一线性枚举无法满足复杂系统的领域划分需求。通过iota配合位移或分组常量,可以实现按模块/领域分组的错误码体系:

package errcode

const (
    // 通用错误码组:0-99
    ErrSuccess       ErrCode = iota // 0
    ErrUnknown                      // 1
    ErrInvalidParam                 // 2
    ErrNotFound                     // 3
    ErrTimeout                      // 4
    ErrPermissionDenied             // 5

    // 用户领域错误码组:100-199
    ErrUserNotFound      ErrCode = iota + 100 // 100
    ErrUserAlreadyExists                       // 101
    ErrUserPasswordIncorrect                   // 102
    ErrUserAccountLocked                       // 103

    // 订单领域错误码组:200-299
    ErrOrderNotFound       ErrCode = iota + 200 // 200
    ErrOrderAlreadyPaid                          // 201
    ErrOrderExpired                              // 202
    ErrOrderStatusInvalid                        // 203
)
分组策略:每个领域分配100个码位(0-99、100-199、200-299…),既保证组间隔离,又为未来扩展预留空间。实际项目中可根据领域复杂度调整组大小(如50或200)。

4.3 错误码命名规范

错误码命名直接影响代码可读性与领域语义的传达。推荐以下规范:

规范维度 规则 示例 反例
前缀 统一使用Err前缀 ErrInvalidParam InvalidParam
领域标识 错误码名包含领域名词 ErrUserNotFound ErrNotFound(歧义)
动词时态 使用过去分词或形容词 ErrOrderAlreadyPaid ErrOrderPay(歧义)
否定形式 避免双重否定 ErrPermissionDenied ErrNotAllowed(模糊)
长度 不超过5个单词 ErrUserAccountLocked ErrUserAccountHasBeenLockedByAdmin

4.4 错误码分组与领域边界关系图

下图展示了错误码分组如何映射到领域边界,以及iota在分组中的自增机制:

错误码分组与领域边界映射 通用错误码组 ErrSuccess (0) ErrUnknown (1) ErrInvalidParam (2) ErrNotFound (3) ErrTimeout (4) 用户领域错误码组 ErrUserNotFound (100) ErrUserAlreadyExists (101) ErrUserPasswordIncorrect (102) ErrUserAccountLocked (103) 订单领域错误码组 ErrOrderNotFound (200) ErrOrderAlreadyPaid (201) ErrOrderExpired (202) ErrOrderStatusInvalid (203) iota 自增机制 每组起始值 = iota + 偏移量(0、100、200),后续常量自动 +1 领域边界隔离 每个领域拥有独立的错误码区间,避免跨领域错误码冲突 新增领域只需分配新的偏移量,不影响已有错误码

4.5 进阶:使用iota+位移实现位域错误码

对于需要组合多个错误状态的场景(如权限校验),可以使用iota配合位移操作定义位域错误码:

package errcode

type PermissionBits uint32

const (
    PermRead  PermissionBits = 1 << iota // 1
    PermWrite                              // 2
    PermExecute                            // 4
    PermDelete                             // 8
    PermAdmin                              // 16
)

// 组合使用
const PermReadWrite = PermRead | PermWrite // 3
注意:位域错误码适用于需要按位组合的场景(如权限、状态标记),不适用于普通业务错误码。普通业务错误码应保持互斥性,避免组合歧义。

4.6 错误码常量定义最佳实践清单

  • 始终从0开始:将ErrSuccess作为零值,符合Go零值语义,避免未初始化错误码产生歧义。
  • 按领域分组:每个领域分配独立的码位区间,区间大小根据领域复杂度预留(建议50-200)。
  • 命名自描述:错误码名称应能直接反映错误原因和所属领域,避免通用名称。
  • 使用Err前缀:统一前缀便于IDE自动补全和代码搜索。
  • 避免魔法数字:所有错误码必须通过常量定义,禁止在业务代码中直接使用数字。
  • 文档化:在常量定义处添加注释,说明每个错误码的触发条件和处理建议。
领域边界断言:错误码分组本质上是领域边界的数值化表达。当跨领域调用返回错误码时,接收方可以通过错误码区间快速判断错误来源,无需深入理解对方领域的内部逻辑。这正是“错误码断言”的核心价值——在数值层面实现领域隔离。