iota 是 Go 语言的一个保留字,用作常量计数器。由于 iota 具有自增特性,所以可以简化数字增长的常量定义。

iota 是一个具有魔法的关键字,往往令初学者难以理解其原理和使用方法。

本文会从书写方法、使用场景、实现原理以及优缺点等各方面剖析 iota 关键字。

1. 书写方法

正确写法:

const (
FirstItem = iota
SecondItem
ThirdItem
)
// 或者
const SingleItem = iota

错误写法:

var FirstItem = iota
// 或者
println(iota)

iota 只能用于常量表达式,而且必须在 const 代码块中出现,不允许出现在其它位置。

2. 使用场景

iota 的主要使用场景用于枚举。Go 语言的设计原则追求极尽简化,所以没有枚举类型,没有 enum关键字。

Go 语言通常使用常量定义代替枚举类型,于是 iota 常常用于其中,用于简化代码。

例如:

package main

const (
B = 1 << (10 * iota) // 1 << (10*0)
KB // 1 << (10*1)
MB // 1 << (10*2)
GB // 1 << (10*3)
TB // 1 << (10*4)
PB // 1 << (10*5)
EB // 1 << (10*6)
ZB // 7 << (10*5)
) func main() {
println(B, KB, MB, GB, TB)
}

输出结果:

1 1024 1048576 1073741824

我们也可以直接这样书写这段代码:

  const (
B = 1
KB = 1024
MB = 1048576
GB = 1073741824
...
)

两段代码对比来看,使用 iota 的代码显然简洁优雅很多。不使用 iota 的代码,对于代码洁癖者来说,简直就是一坨,不可接受。

而 Go 语言的发明者,恰恰具有代码洁癖,而且还是深度洁癖。Go 语言设计初衷之一:追求简洁优雅。

3. iota 原理

iota 源码在 Go 语言代码库中,只有一句定义语句,位于内建文件 go/src/builtin/builtin.go 中:

const iota = 0 // Untyped int.

iota 是一个预声明的标识符,它的值是 0。 在 const 常量声明中,作为当前 const 代码块中的整数序数。

从 Go 语言代码库的代码看,iota 只是一个简单的整数 0,为什么能够作为常量计数器,进行常量自增呢?它的源码到底在哪里?

我们做一个小试验,就会理解其中的道理,看一段代码:

package main

const (
FirstItem = iota
SecondItem
ThirdItem
) func main() {
println(FirstItem)
println(SecondItem)
println(ThirdItem)
}

非常简单,就是打印 FirstItem,SecondItem,ThirdItem。

编译上述代码:

go tool compile -N -l main.go

使用 -N -l 编译参数用于禁止内联和优化,防止编译器优化和简化代码,弄乱次序。这样便于阅读汇编代码。

导出汇编代码:

go tool objdump main.o

截取部分结果如下:

TEXT %22%22.main(SB) gofile../Users/wangzebin/test/test/main.go
...
main.go:10 MOVQ $0x0, 0(SP) // 对应源码 println(FirstItem)
main.go:10 CALL 0x33b [1:5]R_CALL:runtime.printint
...
main.go:11 MOVQ $0x1, 0(SP) // 对应源码 println(SecondItem)
main.go:11 CALL 0x357 [1:5]R_CALL:runtime.printint
...
main.go:11 MOVQ $0x2, 0(SP) // 对应源码 println(ThirdItem)
main.go:11 CALL 0x373 [1:5]R_CALL:runtime.printint
...

编译之后,对应的常量 FirstItem、SecondItem 和 ThirdItem,分别替换为$0x0、$0x1 和 $0x2。

这说明:Go代码中定义的常量,在编译时期就会被替换为对应的常量。当然 iota,也不可避免地在编译时期,按照一定的规则,被替换为对应的常量。

所以,Go 语言源码库中是不会有 iota 源码了,它的魔法在编译时期就已经施展完毕。也就是说,解释 iota 的代码包含在 go 这个命令和其调用的组件中。

如果你要阅读它的源码,准确的说,阅读处理 iota 关键字的源码,需要到 Go 工具源码库中寻找,而不是 Go 核心源码库。

4. iota 规则

使用 iota,虽然可以书写简洁优雅的代码,但对于不熟悉规则的人来讲,又带来的很多不必要的麻烦和误解。

对于引入 iota,到底好是不好,每个人都有自己的评价。实际上,有些不常用的写法,甚至有些卖弄编写技巧的的写法,并不是设计者的初衷。

大多数情况下,我们还是使用最简单最明确的写法,iota 只是提供了一种选择而已。一个工具使用的好坏,取决于使用它的人,而不是工具本身。

以下是 iota 编译规则:

1) 依赖 const

iota 依赖于 const 关键字,每次新的 const 关键字出现时,都会让 iota 初始化为0。

const a = iota // a=0
const (
b = iota // b=0
c // c=1
)

2) 按行计数

iota 按行递增加 1。

const (
a = iota // a=0
b // b=1
c // c=2
)

3) 多个iota

同一 const 块出现多个 iota,只会按照行数计数,不会重新计数。

  const (
a = iota // a=0
b = iota // b=1
c = iota // c=2
)

与上面的代码完全等同,b 和 c 的 iota 通常不需要写。

4) 空行处理

空行在编译时期首先会被删除,所以空行不计数。

  const (
a = iota // a=0 b // b=1
c // c=2
)

5) 跳值占位

占位 "_",它不是空行,会进行计数,起到跳值作用。

  const (
a = iota // a=0
_ // _=1
c // c=2
)

6) 开头插队

开头插队会进行计数。

const (
i = 3.14 // i=3.14
j = iota // j=1
k = iota // k=2
l // l=3
)

7) 中间插队

中间插队会进行计数。

const (
i = iota // i=0
j = 3.14 // j=3.14
k = iota // k=2
l // l=3
)

8) 一行多个iota

一行多个iota,分别计数。

const (
i, j = iota, iota // i=0,j=0
k, l // k=1,l=1
)

参考资料:

1. go语言编程

2. 编程宝库

Go iota 原理和源码剖析的更多相关文章

  1. [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等

    本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ...

  2. Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等

    本课主题 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 Spark Worke ...

  3. Go defer 原理和源码剖析

    Go 语言中有一个非常有用的保留字 defer,它可以调用一个函数,该函数的执行被推迟到包裹它的函数返回时执行. defer 语句调用的函数,要么是因为包裹它的函数执行了 return 语句,到达了函 ...

  4. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  5. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  6. [Spark內核] 第41课:Checkpoint彻底解密:Checkpoint的运行原理和源码实现彻底详解

    本课主题 Checkpoint 运行原理图 Checkpoint 源码解析 引言 Checkpoint 到底是什么和需要用 Checkpoint 解决什么问题: Spark 在生产环境下经常会面临 T ...

  7. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  8. Dubbo原理和源码解析之标签解析

    一.Dubbo 配置方式 Dubbo 支持多种配置方式: XML 配置:基于 Spring 的 Schema 和 XML 扩展机制实现 属性配置:加载 classpath 根目录下的 dubbo.pr ...

  9. Dubbo原理和源码解析之“微内核+插件”机制

    github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...

随机推荐

  1. 解决报错:Unable to process Jar entry [org/springframework/jmx/export/annotation/*****]

    情况说明:从gitub上clone的maven项目,pox.xml配置中的依赖,自己的repository都有,所以正常update project ,正常clean,install,整个过程无报错 ...

  2. Linux 下 xargs 命令

    xargs 常常被大家忽略的一个命令,对它的一些用法很多人可能不熟悉,其实它是一个功能强大的命令,特别是在结合管道进行批量处理方面 语法 xargs 语法格式如下 xargs [OPTION]... ...

  3. t-SNE算法

    t-SNE 算法 前言 t-SNE(t-distributed stochastic neighbor embedding) 是用于降维的一种机器学习算法,由 Laurens van der Maat ...

  4. WEB 标准以及 W3C 的理解与认识

    01.  WEB标准 ①  web标准 简单来说可以分为结构.表现和行为. ②  结构:主要是有HTML标签组成(通俗点说,在页面body里面我们写入的标签都是为了页面的结构)   表现:即指css样 ...

  5. 重修 Tarjan

    Tarjan是谁 Tarjan's SCCs(有向图强连通分量)algorithm 给定⼀个有向图 \(G\),若存在 \(rt\in V\),满⾜从 \(rt\) 出发能到达 \(V\) 中的所有的 ...

  6. LOJ6469 Magic(trie)

    纪念我菜的真实的一场模拟赛 首先看到这个题目,一开始就很毒瘤.一定是没有办法直接做的. 我们考虑转化问题 假设,我们选择枚举\(x\),其中\(x\)是\(10\)的若干次方,那么我们只需要求有多少对 ...

  7. Miller-Rabin学习笔记

    首先给出两个定理: 1.费马小定理 设p是一个素数,a是一个整数,且不是p的倍数,那么 \(a^{p−1} \equiv\ 1 \pmod p\) 2.二次探测定理 若\(p\)是素数,\(x\)是一 ...

  8. [对对子队]会议记录4.20(Scrum Meeting11)

    今天已完成的工作 何瑞 ​ 工作内容:搭建第三关,添加了运行指令标识 ​ 相关issue:搭建关卡2.3 ​ 相关签入:4.20签入1 4.20签入2 吴昭邦 ​ 工作内容:搭建第三关 ​ 相关iss ...

  9. OO_JAVA_JML系列第三次作业__架构之谈

    OO_JAVA_JML系列第三次作业 ## ----架构之谈 目录 OO_JAVA_JML系列第三次作业 出发点 操作的可分离性 操作本身的多样性 实现手段:表驱动编程 储存 注册 出发点 操作的可分 ...

  10. TDengine在数益工联工业物联采集平台建设中的初步实践

    作者:易永耀 夏杭泰 邓炜兴 公司介绍 数益工联致力于打造基于数据流+价值流的离散制造业数字化软件:应用新一代的物联网技术与丰富的现场交互手段,融合工业工程精益思想,为离散制造业客户的数字化升级提供从 ...