关于golang:Go-iota-原理和源码剖析

4次阅读

共计 2634 个字符,预计需要花费 7 分钟才能阅读完成。

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. 编程宝库
正文完
 0