Go中枚举靠const+iota+自定义类型实现,iota仅在参与表达式时递增,非iota赋值会重置计数;位掩码需用1<<iota避免冲突。

Go 里没有 enum 关键字,所谓“枚举”就是靠 const 块 + iota 搭配自定义类型来实现的。不这么写,就只是裸整数,编译器拦不住非法值,日志里全是数字,JSON 序列化出来也是 0/1/2——根本没法用。
为什么 iota 值突然不对了?
iota 不是每行自动加 1 的计数器,而是“每次在 const 块中被实际参与表达式计算时才递增”。空行、注释、下划线 _ 占位都不触发递增;但只要某一行写了非 iota 的值(比如 Unknown = -1 或 Label = "foo"),后续所有 iota 就重置为 0。
- 错误写法:
const ( Unknown = -1 Active = iota // ← 这里 iota 是 0,不是 1 Inactive // ← 1 ) - 正确做法:把非
iota值单独拎出去,或用占位:const ( _ = iota // 强制起始为 0,且占掉第一行 Unknown = -1 // 显式赋值,不干扰 iota 流 Active Inactive ) - 更稳妥的方式:每个语义组用独立
const块,避免混用
位掩码枚举怎么写才不翻车?
直接用 iota 得到的是 0,1,2,3…,不能当位掩码用。比如 Read = iota 是 0,Write 是 1,Read | Write 结果是 1,和 Write 值冲突——逻辑全崩。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
- 必须用位移:
const ( Read = 1 << iota // 1 Write // 2 Exec // 4 Delete // 8 ) - 类型要显式指定为
uint或自定义无符号类型,避免有符号溢出:type Perm uint,再让常量属于该类型 - 判断是否含某权限,别用
==,要用&:if p & Read != 0 - 别在一个
const块里混入字符串、浮点数或函数调用,否则iota下一行归零
为什么 fmt.Println 输出的是数字而不是名字?
因为没实现 String() string 方法。iota 生成的只是整数,Go 不会自动映射成字符串。不加这个方法,fmt.Printf("%v", Active) 打印的就是 1,不是 "active"。
立即学习“go语言免费学习笔记(深入)”;
- 必须为自定义类型实现:
func (s Status) String() string { switch s { case Pending: return "pending" case Running: return "running" case Finished: return "finished" default: return "status(" + strconv.Itoa(int(s)) + ")" } - 注意:这个方法只影响
fmt包行为,不影响json.Marshal——后者默认还是输出数字,如需 JSON 输出字符串,得额外实现MarshalJSON - 别漏掉
default分支,否则未知值会 panic 或返回空字符串
最常被忽略的一点:类型声明必须显式绑定,比如 type Status int,然后 Pending Status = iota。如果只写 Pending = iota,那它只是无类型的 int,传给需要 Status 类型的函数时会编译失败,而且无法做类型约束校验。

















