Go - Constant
原生
概述
- Go 常量是一种在
源码编译期间被创建的语法元素- 该元素的值可以像变量那么被
初始化,但初始化表达式必须在编译期可以求值
- 该元素的值可以像变量那么被
- Go 常量一旦
声明并被初始化,它的值在整个程序的生命周期内便保持不变- 在
并发设计时不需要考虑常量访问的同步 - 并且被创建并初始化后的常量可以作为其它常量的初始化表达式的一部分
- 在
Go 引入
const关键字声明常量,与var类似
1 | const Pi float64 = 3.14159265358979323846 |
Go 常量的类型仅限于
基本数据类型:数值类型、字符串类型、布尔类型
C
C 语言
原生不支持常量
- 在 C 语言中,
字面值担负着常量的角色 - 为了不让
字面值以魔数的形式分布于源码各处,早期 C 语言的常用实践是使用宏(macro),即宏定义常量 - 使用
宏定义常量一直是 C 编码中的主流风格,即便后续的C 标准中提供了const关键字
1 |
宏定义常量的问题 - 预编译阶段
宏替换
- 宏定义常量仅仅只是一种在
预编译阶段进行替换的字面值,继承了宏替换的复杂性和易错性 - 另外还有
类型不安全、无法在调试时通过宏名字输出常量的值等问题
C const
- const 关键字修饰的标识符本质上依旧是
变量
Go
- Go
原生提供的用const定义的常量 - 整合了 C 语言中的三种常量形式:
宏定义常量、const修饰的只读变量、枚举常量- Go 仅通过
const + iota来统一实现
- Go 仅通过
- Go 常量是
类型安全的,并且对编译器优化友好
创新
无类型常量
即便两个类型拥有
相同的底层类型,但仍然是不同的数据类型,不能相互比较或者混在一个表达式中进行计算
1 | type MyInt int |
显式转型
1 | type MyInt int |
无类型常量 - 无类型常量也有
默认类型,依据初始值来决定
1 | type MyInt int |
隐式转型
- 对于
untyped constant参与的表达式求值编译器根据上下文的类型信息,将 untyped constant自动转换为相应的类型后,再参与求值计算 -隐式进行
- 由于隐式转型的对象是一个
常量,因此不会引发类型安全问题,Go编译器会保证转型安全性
如果 Go 编译器在做隐式转换时,发现无法将常量转换为目标类型,Go 编译器也会报错
1 | const m = 1333333333 |
简化代码:无类型常量 + 常量隐式转型
实现枚举
Go 常量
应用最广泛的领域
- Go
原生不支持枚举类型,可以通过是使用const 代码块定义的常量集合来实现枚举 - Go 的设计者在语言设计之初,就希望将
枚举类型和常量合而为一,无需再单独提供枚举类型- 但造成的缺陷是,枚举仅仅支持
基本类型
- 但造成的缺陷是,枚举仅仅支持
- Go 将
C 枚举类型特性移植到了Go 常量特性,并进行了改良
C
在 C 语言中,枚举是一个
命名的整型常数的集合
如果没有
显式给枚举常量赋初始值,那么枚举类型的第一个常量值就是0,后续常量的值再依次+1
1 | enum Weekday { |
Go
Go 没有直接继承 C 的枚举特性
自动重复上一非空行- 引入 const 块中的
行偏移量指示器 iota
Go const 语法提供了机制:
隐式重复前一个非空表达式
1 | const ( |
iota
iota是 Go 的一个预定义标识符- 表示的是
const 声明块(包括单行声明)中,每个常量所处位置在块中的偏移量(从0开始)
- 表示的是
- 每一行中
iota自身也是一个untyped constant- 会发生隐式转型
1 | const ( |
位于同一行的
iota即便出现多次,多个iota的值也是一样的
1 | const ( |
跳过
iota = 0
1 | const ( |
跳过某几个值
1 | const ( |
iota降低了枚举的维护成本
传统
1 | const ( |
iota
1 | const ( |
每个
const代码块的iota是独立变化的(iota的生命周期为const 代码块)
1 | const single = iota // 0, iota = 0 |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.











