枚举的存在代表了一类现实需求:
- 有限数量标识符构成的集合,且多数情况下并不关心集合中标识符实际对应的值
- 注重类型安全
Go 语言没有提供定义枚举常量的语法。我们通常使用常量语法定义枚举常量,比如用 Go 来定义上面的 Weekday:
const (Sunday = 0Monday = 1Tuesday = 2Wednesday = 3Thursday = 4Friday = 5Saturday = 6)
Go 的 const 语法提供了“隐式重复前一个非空表达式”的机制,比如下面代码:
const (Apple, Banana = 11, 22Strawberry, GrapePear, Watermelon)
常量定义的后两行没有显式给予初始赋值,Go 编译器将为其隐式使用第一行的表达式,这样上述定义等价于:
const (Apple, Banana = 11, 22Strawberry, Grape = 11, 22Pear, Watermelon = 11, 22)
不过这显然无法满足“枚举”的要求。Go 在这个机制的基础上又提供了iota“神器”,有了 iota,我们就可以定义满足各种场景的枚举常量了。
iota 是 Go 语言的一个预定义标识符,它表示的含义是 const 声明块(包括单行声明)中每个常量所处位置在块中的偏移值(从零开始)。同时,每一行中的 iota 自身也是一个无类型常量,可以像上一节所提到的无类型常量那样自动参与到不同类型的求值过程中,而无需对其进行显式转型操作。
**
下面是 Go 标准库中 sync/mutex.go 中的一段枚举常量的定义:
// $GOROOT/src/sync/mutex.go (go 1.12.7)const (mutexLocked = 1 << iota // mutex is lockedmutexWokenmutexStarvingmutexWaiterShift = iotastarvationThresholdNs = 1e6)
- mutexLocked = 1 << iota 这里是 const 声明块的第一行,iota 的值是该行在 const 块中的偏移,因此 iota 的值为 0,我们得到 mutexLocked 这个常量的值为 1 << 0,即 1;
- mutexWorken 这里是 const 声明块的第二行,由于没有显式的常量初始化表达式,根据 const 声明块的“隐式重复前一个非空表达式”的机制,该行等价于 mutexWorken = 1 << iota。该行为 const 块中的第二行,因此偏移量 iota 的值为 1,我们得到 mutexWorken 这个常量的值为 1<< 1,即 2;
- mutexStarving 该常量同 mutexWorken,该行等价于 mutexStarving = 1 << iota,由于在该行的 iota 的值为 2,因此我们得到 mutexStarving 这个常量的值为 1 << 2,即 4;
- mutexWaiterShift = iota 这一行的常量初始化表达式与前三行不同,由于该行为第四行,iota 的偏移值为 3,因此 mutexWaiterShift 的值就为 3。
位于同一行的 iota 即便出现多次,其值也是一样的:
const (Apple, Banana = iota, iota + 10 // 0, 10 (iota = 0)Strawberry, Grape // 1, 11 (iota = 1)Pear, Watermelon // 2, 12 (iota = 2))
如果我们要略过 iota = 0,而从 iota = 1 开始正式定义枚举常量,我们可以效仿下面代码:
// $GOROOT/src/syscall/net_js.go go 1.12.7const (_ = iotaIPV6_V6ONLYSOMAXCONNSO_ERROR)
如果我们要略过某一行,也可以使用类似方式:
const (_ = iota // 0Pin1Pin2Pin3_Pin5 // 5)
iota 让你维护枚举常量“列表”更容易
const (Black = 1Red = 2Yellow = 3)// 新增需要手动+1const (Blue = 1Black = 2Red = 3Yellow = 4)// 使用 iota 无需更多更改const (_ = iotaBlueRedYellow)
使用有类型枚举常量保证类型安全。
// $GOROOT/src/time/time.gotype Weekday intconst (Sunday Weekday = iotaMondayTuesdayWednesdayThursdayFridaySaturday)
