
Go 里没有 enum 关键字,iota 是唯一原生枚举机制
Go 不提供 enum 语法,所谓“枚举”全靠 const 块 + iota 实现。它不是类型系统的一部分,而是一种编译期整数自增工具——值从 0 开始,每换一行就 +1。
常见错误是以为 iota 会自动绑定到某个类型上,结果定义了一堆 int 常量,却忘了加类型约束,导致后续赋值或比较时类型不匹配。
-
iota只在const块内有效,且按行计数,空行、注释行不影响计数 - 想让枚举从 1 开始?写
const ( A = iota + 1; B ),别写_; A; B——后者 A 是 1,但 _ 占位会让可读性变差 - 多个 const 块之间
iota互不影响,每个块都重置为 0
给 iota 枚举加类型,避免隐式 int 转换出错
直接写 const ( A; B ),A 和 B 类型都是 untyped int,一旦用在需要明确类型的上下文(比如结构体字段、函数参数),就会报 cannot use A (type int) as type MyEnum 这类错误。
正确做法是在第一个常量显式声明类型,后续同块内常量自动继承:
立即学习“go语言免费学习笔记(深入)”;
type Status int const ( Pending Status = iota // 显式指定类型 Running Finished )
这样 Pending、Running 都是 Status 类型,能安全用于字段、方法接收者、switch 分支等场景。
- 别用
type Status int64然后让iota赋值——iota默认是 int,跨平台时可能溢出;如需大整数,显式转:Pending Status = iota + 1 - 如果枚举值需要非连续数字(比如 HTTP 状态码),不能只靠
iota,得手动赋值:NotFound = 404,此时iota在那一行失效
字符串枚举:用 String() 方法实现 fmt.Println 友好输出
默认打印 Pending 输出的是数字 0,而不是 "Pending"。要让它可读,必须为枚举类型实现 String() string 方法。
注意:这个方法只影响 fmt 包的打印行为,不影响 JSON 序列化或数据库存取——那些需要额外处理。
func (s Status) String() string {
switch s {
case Pending:
return "pending"
case Running:
return "running"
case Finished:
return "finished"
default:
return "unknown"
}
}
- 没实现
String()时,fmt.Printf("%v", Pending)打印 0;实现了才打印 "pending" - 别漏掉
default分支,否则遇到非法值(比如从 DB 读到 999)会 panic 或返回空字符串 - 如果枚举值很多,建议用 map 初始化,但要注意初始化时机——不能在包级变量中直接用 map literal 绑定未定义的常量
JSON 序列化枚举:必须实现 MarshalJSON 和 UnmarshalJSON
Go 的 json.Marshal 默认把枚举当整数序列化,比如 {"status": 0};前端或日志系统看不懂。要输出 {"status": "pending"},必须手动实现两个方法。
常见坑是只实现了 MarshalJSON,忘了 UnmarshalJSON,导致反序列化失败,报 json: cannot unmarshal string into Go value of type Status。
-
MarshalJSON返回[]byte,推荐用json.Marshal包装字符串,别手拼引号 -
UnmarshalJSON接收[]byte,先尝试解析为字符串,再查表映射;若失败,再尝试解析为数字(兼容旧数据) - 别在
UnmarshalJSON里用switch string(b)——b是原始字节,可能含空格或引号,要用strings.Trim或json.Unmarshal先解出字符串
String() 和 JSON 方法。










