2.3 iota与枚举
iota在Go语言中是追求极简的典型案例。iota并非一个或多个英文单词的缩写,而是希腊字母表中的第9个字母“ι”(中文音译为约塔)。在书写上,是最为简单的字符,因此引申为“最简单”这一语义。Go语言中的iota只是一个常量表达式。在一个const块中,iota代表了当前行的索引值(从0开始)。iota往往出现在常量定义中,尤其是用来定义枚举。
2.3.1 iota实现自增
1.利用iota来定义累加的常量
在如下代码段中,我们为周日至周六依次定义了枚举常量Sunday~Saturday,并依次为其赋值为0~6。
那么,我们可以利用iota来进行形式上的简化,如代码清单2-5所示。
代码清单2-5 利用iota定义常量
执行该代码段,控制台的输出如下:
0 1 2 3 4 5 6
在该代码段中,利用iota将常量Sunday~Saturday赋予从0开始的、自增的正整数。利用这种写法可以最大限度地避免手写代码出错。
2.iota的极简形式
为了体现iota极简的特性,代码清单2-5还可以修改为代码清单2-6所示的形式。
代码清单2-6 连续表达式精简iota的使用
正如2.2.2节中所提到的连续常量的简写形式,代码清单2-6的常量块中只保留了第一个iota赋值操作,后续常量省略了所有的“=iota”,同样可以实现自增赋值的功能。
2.3.2 iota计数不会中断
iota如同一个代码行计数器,即使在常量组的中间位置中断了iota的引用,计数器也会随着代码行数而自增下去。代码清单2-7演示了这一场景。
代码清单2-7 iota表达式的中断
在该代码段中,我们特意将常量Wednesday设置为一个字符串“x”,而后续的Thursday、Friday、Saturday仍然保持为iota。执行该代码,在控制台上打印的内容如下:
0 1 2 x 4 5 6
通过输出可以看出,当iota第一次出现时,其值为0;后续代码每增加一行,iota如同一个计数器,均会自动加1。Wednesday = "x"只是表示iota代表的计数器没有被使用,并不影响计数器的继续累加。
但如果我们将代码段修改为代码清单2-8所示的样子,则执行结果会完全不同。
代码清单2-8 iota表达式被中断,且不再使用
该代码段的执行结果如下:
0 1 2 x x x x
因为Wednesday = "x"之后的常量未显式赋值,所以直接默认为Wednesday(距离最近的上一个常量)的值,即字符串“x”。
2.3.3 iota的使用场景
从前面的讲述可以看出,iota的主要使用场景为常量块中的连续常量。因此,连续性是iota的主要特点。这也引发了另外一个问题:一旦在现有常量中间插入一行代码,就会导致原有的常量值发生改变。例如,一个表示状态机的常量组定义如下:
在该常量块中,顺序定义了4种状态,并为其赋值0~3。表面看上去,一切都很完美,但是后续开发者期望增加一个状态PAUSE,并将它插入RUNNING和FINSIH之间,则会导致FINISH的值发生变更。例如:
对比两段代码可知,FINISH的值会由3变更为4。对于开发者而言,有时很难意识到插入一个新的常量定义可能会导致的骨牌效应。例如,数据库中的一张任务表使用该枚举值作为状态字段,并且已有部分数据持久化到了数据库中,那么,在增加了PAUSE状态后,很多任务的状态数据将与业务意义不再匹配。
对于如下明确定义的代码:
此时,再增加一个新的状态PAUSE,大多数开发者会意识到不能占用已被分配的值,由此出错的概率也会大大减小。因此,iota虽然带来了一定的方便,但对于开发者而言,代码也变得更加隐晦。
那么,到底怎样的场景才适合使用iota定义呢?笔者认为,常量组需要具备以下两个特点:
(1)连续性:要定义的各个常量值具备连续性,是连续的正整数。
(2)数据固化:这些常量是固定且不易发生变更的,例如,周一至周日,1月至12月等。
综上所述,虽然笔者花费了较多的笔墨来讲述iota,但是在实际开发中对于iota的使用需要非常谨慎。毕竟,相较于业务的稳定性,iota带来的便利可以忽略不计。