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.