Go 的 iota
怎么用?在这篇文章中,我们将了解 Go 中的“iota”标识符、它的使用方式以及何时不使用它。
“iota”标识符用于表示 Go 中基于整数的常量,是声明常量序列的便捷方式,同时保持代码可读性。
使用 Iota 进行常量声明
通常,常量可以通过指定其值来声明,例如:
const (
SPADES = 1
HEARTS = 2
DIAMONDS = 3
CLUBS = 4
)
func main() {
fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
}
输出:
SPADES: 1 HEARTS: 2 DIAMONDS: 3 CLUBS: 4
然而,我们可以使用 iota
并避免显式声明增量整数值:
const (
SPADES = iota
HEARTS = iota
DIAMONDS = iota
CLUBS = iota
)
func main() {
fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
}
输出:
SPADES: 0 HEARTS: 1 DIAMONDS: 2 CLUBS: 3
从这个例子中,我们可以观察到 iota
的两个属性:
- iota 的值随着每个常量声明而递增
- 它从
0
开始
我们也可以在第一个 iota
之后省略后面的 iota
声明,所以这段代码会给出相同的结果:
const (
SPADES = iota
HEARTS
DIAMONDS
CLUBS
)
从非零索引开始
正如我们在前面的示例中看到的,iota
从 0
开始,但我们可以通过将给定数量添加到第一个声明来更改基值。
因此,如果我们想要与第一个示例相同的结果,其中 SPADES = 1
,那么我们可以如下声明常量:
const (
// 现在,索引从 1 开始
SPADES = iota + 1
HEARTS
DIAMONDS
CLUBS
)
输出:
SPADES: 1 HEARTS: 2 DIAMONDS: 3 CLUBS: 4
一般来说,如果我们想从索引 n
开始,我们可以用 iota + <n>
开始常量声明
跳过值
请记住,每个常量声明的 iota
值都会递增。所以,如果我们想跳过一个值,我们可以声明一个空常量值:
const (
SPADES = iota
HEARTS
_ // iota 的值会增加,因为这仍然是一个常量声明
DIAMONDS
CLUBS
)
输出:
SPADES: 0 HEARTS: 1 DIAMONDS: 3 CLUBS: 4
你可以添加任意数量的空常量声明,以跳过前面的多个值。
自定义数学运算
虽然 iota
的值是递增的,但我们可以利用基本的数学运算来修改分配给每个常量的最终值。
例如,如果我们使用 iota * 10
,它将为每个 const 声明给出 10 的第 n 个倍数:
const (
ZERO = 10 * iota // = 10 * 0
TEN // = 10 * 1
TWENTY // = 10 * 2
THIRTY // = 10 * 3
)
func main() {
fmt.Println("ZERO:", ZERO, "TEN:", TEN, "TWENTY:", TWENTY, "THIRTY:", THIRTY)
}
输出:
ZERO: 0 TEN: 10 TWENTY: 20 THIRTY: 30
类似地,我们可以使用左移操作来进行指数递增:
const (
BYTE = 1 << (iota * 10) // = 1 << 0
KILOBYTE // = 1 << 10
MEGABYTE // = 1 << 20
GIGABYTE // = 1 << 30
)
func main() {
fmt.Println("BYTE:", BYTE, "KILOBYTE:", KILOBYTE, "MEGABYTE:", MEGABYTE, "GIGABYTE:", GIGABYTE)
}
输出:
BYTE: 1 KILOBYTE: 1024 MEGABYTE: 1048576 GIGABYTE: 1073741824
根据官方文档定义,在同一个 ConstSpec 中多次使用 iota 都具有相同的值:
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0 (iota == 0)
bit1, mask1 // bit1 == 2, mask1 == 1 (iota == 1)
_, _ // (iota == 2, unused)
bit3, mask3 // bit3 == 8, mask3 == 7 (iota == 3)
)
重置 Iota 值
iota 值仅在常量声明块内递增。因此,一旦我们在不同的声明块中声明了另一组常量,iota 值就会重置。
例如,我们可以在同一文件中声明前面示例中讨论的所有常量:
const (
SPADES = iota
HEARTS
DIAMONDS
CLUBS
)
// iota 在这里重置
const (
ZERO = 10 * iota
TEN
TWENTY
THIRTY
)
// iota 在这里重置
const (
BYTE = 1 << (iota * 10) // = 1 << 0
KILOBYTE // = 1 << 10
MEGABYTE // = 1 << 20
GIGABYTE // = 1 << 30
)
func main() {
fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
fmt.Println("ZERO:", ZERO, "TEN:", TEN, "TWENTY:", TWENTY, "THIRTY:", THIRTY)
fmt.Println("BYTE:", BYTE, "KILOBYTE:", KILOBYTE, "MEGABYTE:", MEGABYTE, "GIGABYTE:", GIGABYTE)
}
输出:
SPADES: 0 HEARTS: 1 DIAMONDS: 3 CLUBS: 4
ZERO: 0 TEN: 10 TWENTY: 20 THIRTY: 30
BYTE: 1 KILOBYTE: 1024 MEGABYTE: 1048576 GIGABYTE: 1073741824
为什么 Iota 是有用的
使用 iota
是声明任意常量或枚举的一种更具可读性和惯用的方式。
使用 iota,我们可以添加更多常量值,而无需考虑要分配的下一个数值。
它还可以帮助我们避免代码中出现某些类型的错误。
为了说明这一点,假设我们有一大堆错误码要声明为常量。在这些情况下,很容易犯两次分配相同值的人为错误:
const (
ERR_ABC = 1
ERR_DEF = 2
ERR_HIJ = 3
ERR_KLM = 3 // 我丢
ERR_NOP = 4
ERR_QRS = 5
)
如果我们使用 iota 来分配这些值,我们就不会遇到这个问题,我们的代码也不容易出错。
什么时候应该避免使用 Iota
当有任意值或增量值用作常量时,最好使用 Iota。
在某些情况下,常量之间没有任何顺序关系。例如,秒、分、小时和天等时间单位可以表示为非顺序常量:
const (
SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
)
在这种情况下,我们无法使用 iota
,因为连续常量之间的关系不是增加相同的数量。
我们不应该使用 iota 的另一种情况是数值不是任意的。例如,表示一副牌中的牌总数,或王牌总数:
const (
TOTAL_CARDS = 52
TOTAL_JOKERS = 2
)
在这种情况下,常量的值是有意义的,而不是顺序的,所以最好不要使用 iota
。
结语
不知你是否好奇过这个 iota
标识符到底是啥意思(不是用法),你也许会认为它是一个短语或单词的缩写,然而 “iota” 并不是某物的首字母缩写词,它本身就是一个词,源自古希腊语 ἰῶτα (iôta)。
“iota” 表示希腊字母表中最小的字母,与 0 是枚举类型的最小值的含义相同,在英语中,它是一个具有反映希腊字母定义的词,类似于 “A” 读作 “alpha”。
iota 的词典定义:https://www.vocabulary.com/dictionary/iota
- 美式发音:
/aɪˈoʊdə/
- 英式发音:
/aɪˈəʊtə/