Go 真的有枚举吗?

开发 后端
代码抽象于现实。程序与生活中关于枚举的概念是相通的:枚举代表一个对象所有可能取值的集合。

[[437193]]

本文转载自微信公众号「Golang技术分享」,作者机器铃砍菜刀。转载本文请联系Golang技术分享公众号。

Go 中有枚举吗?这是一个模棱两可的问题。有人说它有,有人说它没有。

什么是枚举

代码抽象于现实。程序与生活中关于枚举的概念是相通的:枚举代表一个对象所有可能取值的集合。例如,表示星期的 SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY 就是一组枚举值。

实际上,我们可以将 Go 中所有原始类型视为一种枚举。例如 bool 类型可以被认为是一个只能为 true 或 false 的枚举;byte 类型是 0 至 255 的枚举;指针是 32 位或 64 位地址空间所有可能的内存地址的枚举。

在例如 Python、Java、C 等语言中,一般都会有enum关键字或类提供于开发者实现枚举。

通用伪代码可表达如下

  1. enum 枚举名{ 
  2.     标识符①[=整型常数], 
  3.     标识符②[=整型常数], 
  4.     ... 
  5.     标识符N[=整型常数], 
  6. }枚举变量; 

Go 没有enum关键字。但我们可以观察枚举的特征:同一组枚举值在定义后不应被改变;枚举值对应的数据类型应该相同;枚举值是有限的;枚举值与其含义是一一对应的。

根据以上特征,在 Go 中可通过const与 iota关键字来实现枚举的诉求。

iota

const用于定义常量,它们在编译期创建,在运行时不能被修改。且仅有布尔型、数字型(整数型、浮点型和复数)和字符串型能被定义为常量。

常量声明格式如下

  1. const identifier [type] = value 

而 iota 是常量计数器,它在遇到 const 关键字时,就被重置为 0。当 const 中每增一行常量声明(包括空白标识符_),iota 计数将加1。

  1. const ( 
  2.  A int = iota   // 0 
  3.  _            
  4.  B              // 2 
  5.  C              // 3 
  6.  D              // 4 
  7.  
  8. const ( 
  9.  E int = iota   // 0 
  10.  F              // 1 

Go 枚举实现

有了iota的参与,在 Go 中想要枚举星期值,我们可以如下定义

  1. type Weekday int 
  2.  
  3. const ( 
  4.  _ Weekday = iota // ignore first value by assigning to blank identifier 
  5.  Sunday 
  6.  Monday 
  7.  Tuesday 
  8.  Wednesday 
  9.  Thursday 
  10.  Friday 
  11.  Saturday 

在使用枚举值过程中,往往有输出打印的需求

  1. fmt.Println(Sunday, Monday)  // 1 2 

但原始的结果很不直观,它不能反映出枚举值背后的含义。我们需要为 Weekday 对象定义输出。

  1. func (w Weekday) String() string { 
  2.  return [...]string{"Sunday""Monday""Tuesday""Wednesday""Thursday""Friday""Saturday"}[w-1] 

在 Go 中,我们可以为任意自定义类型绑定String()方法,使其按照String()方法中定义的格式进行打印。

  1. func main() { 
  2.  var day = Monday 
  3.  switch day { 
  4.  case Monday, Tuesday, Wednesday, Thursday, Friday: 
  5.   fmt.Printf("今天是%s,加油!打工人"day
  6.  case Saturday, Sunday: 
  7.   fmt.Printf("今天是%s,好好休息!打工人"day
  8.  default
  9.   fmt.Println("不存在的一天"
  10.  } 

执行结果

  1. 今天是Monday,加油!打工人 

Go 枚举实现的不足

上述方案看似已经实现了枚举功能,但其实存在一些问题。

首先,由于 iota 基于 int 类型,这意味着在程序中,任何整数都可以转为枚举类型(这也是为何我们上文switch的case 中会有default分支),但这并不是我们想要的。

  1. func main() { 
  2.  fakeNum := 8 
  3.  day := Weekday(fakeNum) 
  4.  fmt.Println(day
  5.  
  6. # go run main.go 
  7. %!v(PANIC=String method: runtime error: index out of range [7] with length 7) 

那善于思考的读者就会想到,既然 int 不行,那我们可以采用字符串常量来表示枚举值啊。但这个方案同样存在上述的问题,而且相较于使用 int 比较,当比较字符串时,需要付出额外的性能成本。

另外,我们对于枚举还有一个很重要的诉求,就是枚举。对应于 Go 循环表达式,枚举迭代的期望是这样

  1. for i, day := range Weekday { 
  2.  ... 

但显然,现在的代码方案满足不了这种诉求。

总结

本文讨论了 Go 目前通过 iota 关键字实现枚举的做法,但这种方式并没有实现完整的枚举功能。在官方 issue 19814 中提出了 Go 中应该增加 enum 关键字的提案,感兴趣的读者可以详细查看。

关于 Go 中的枚举实现,你有不一样的观点吗,欢迎留言讨论。

参考

 

proposal: spec: add typed enum support: https://github.com/golang/go/issues/19814

 

责任编辑:武晓燕 来源: Golang技术分享
相关推荐

2023-12-26 08:17:04

Go框架Go 语言

2013-08-07 14:30:33

微信5.0

2012-07-03 15:04:22

程序员

2021-05-18 11:14:55

人脸识别人工智能技术

2012-04-16 10:15:13

JQuery插件开发

2022-08-15 08:06:49

Go语言类型

2022-05-11 09:04:50

Go函数数组

2022-01-05 12:03:48

MySQL索引数据

2024-04-07 00:00:05

事务Java开发

2023-04-21 10:33:42

2010-03-03 14:35:49

Android智能手机

2019-10-11 14:43:55

Windows电脑硬盘分区

2020-03-13 14:45:14

Java枚举代码

2021-10-12 05:49:13

微信App访问相册

2021-06-09 07:15:20

Go枚举技巧

2021-01-20 16:26:17

Go编程语言

2022-07-26 00:00:22

HTAP系统数据库

2020-01-15 10:17:41

Kubernetes容器负载均衡

2019-06-14 14:48:41

多云云计算云平台

2014-04-17 16:42:03

DevOps
点赞
收藏

51CTO技术栈公众号