Go 中有枚举吗?
Go 中有枚举吗?这是一个模棱两可的问题。有人说它有,有人说它没有。
什么是枚举
代码抽象于现实。程序与生活中关于枚举的概念是相通的:枚举代表一个对象所有可能取值的集合。例如,表示星期的 SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY 就是一组枚举值。
实际上,我们可以将 Go 中所有原始类型视为一种枚举。例如 bool 类型可以被认为是一个只能为 true 或 false 的枚举;byte 类型是 0 至 255 的枚举;指针是 32 位或 64 位地址空间所有可能的内存地址的枚举。
在例如 Python、Java、C 等语言中,一般都会有enum
关键字或类提供于开发者实现枚举。
通用伪代码可表达如下
enum 枚举名{
标识符①[=整型常数],
标识符②[=整型常数],
...
标识符N[=整型常数],
}枚举变量;
Go 没有enum
关键字。但我们可以观察枚举的特征:同一组枚举值在定义后不应被改变;枚举值对应的数据类型应该相同;枚举值是有限的;枚举值与其含义是一一对应的。
根据以上特征,在 Go 中可通过const
与 iota
关键字来实现枚举的诉求。
iota
const
用于定义常量,它们在编译期创建,在运行时不能被修改。且仅有布尔型、数字型(整数型、浮点型和复数)和字符串型能被定义为常量。
常量声明格式如下
const identifier [type] = value
而 iota 是常量计数器,它在遇到 const 关键字时,就被重置为 0。当 const 中每增一行常量声明(包括空白标识符_
),iota 计数将加1。
const (
A int = iota // 0
_
B // 2
C // 3
D // 4
)
const (
E int = iota // 0
F // 1
)
Go 枚举实现
有了iota
的参与,在 Go 中想要枚举星期值,我们可以如下定义
type Weekday int
const (
_ Weekday = iota // ignore first value by assigning to blank identifier
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
在使用枚举值过程中,往往有输出打印的需求
fmt.Println(Sunday, Monday) // 1 2
但原始的结果很不直观,它不能反映出枚举值背后的含义。我们需要为 Weekday 对象定义输出。
func (w Weekday) String() string {
return [...]string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}[w-1]
}
在 Go 中,我们可以为任意自定义类型绑定String()
方法,使其按照String()
方法中定义的格式进行打印。
func main() {
var day = Monday
switch day {
case Monday, Tuesday, Wednesday, Thursday, Friday:
fmt.Printf("今天是%s,加油!打工人", day)
case Saturday, Sunday:
fmt.Printf("今天是%s,好好休息!打工人", day)
default:
fmt.Println("不存在的一天")
}
}
执行结果
今天是Monday,加油!打工人
Go 枚举实现的不足
上述方案看似已经实现了枚举功能,但其实存在一些问题。
首先,由于 iota 基于 int 类型,这意味着在程序中,任何整数都可以转为枚举类型(这也是为何我们上文switch
的case
中会有default
分支),但这并不是我们想要的。
func main() {
fakeNum := 8
day := Weekday(fakeNum)
fmt.Println(day)
}
# go run main.go
%!v(PANIC=String method: runtime error: index out of range [7] with length 7)
那善于思考的读者就会想到,既然 int 不行,那我们可以采用字符串常量来表示枚举值啊。但这个方案同样存在上述的问题,而且相较于使用 int 比较,当比较字符串时,需要付出额外的性能成本。
另外,我们对于枚举还有一个很重要的诉求,就是迭代。对应于 Go 循环表达式,枚举迭代的期望是这样
for i, day := range Weekday {
...
}
但显然,现在的代码方案满足不了这种诉求。
总结
本文讨论了 Go 目前通过 iota 关键字实现枚举的做法,但这种方式并没有实现完整的枚举功能。在官方 issue 19814 中提出了 Go 中应该增加 enum 关键字的提案,感兴趣的读者可以详细查看。
关于 Go 中的枚举实现,你有不一样的观点吗,欢迎留言讨论。
参考
proposal: spec: add typed enum support: https://github.com/golang/go/issues/19814
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
2020-11-30 MySQL 主从同步(3)-percona-toolkit工具(数据一致性监测、延迟监控)
2020-11-30 监控MySQL主从同步是否异常,如果异常,则发送短信或者邮件给管理员
2020-11-30 rpm error: %preun(xxx) scriptlet failed, exit status1
2020-11-30 Nginx禁止ip访问或非法域名访问
2020-11-30 MySQL 手动主从同步不锁表
2017-11-30 SVN 错误:Error validating server certificate for 'https://xxxxxxx':443... Mac os svn客户端证书验证缓存 解决
2017-11-30 nginx location详解