常量与 iota
本教程共 80 篇 · 第 9 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
9. 常量与 iota
本节目标:学会声明常量,理解无类型常量的妙处,掌握 iota 枚举器的各种玩法。
什么是常量
常量是程序运行期间不会改变的值。和变量相对,变量可以改,常量不能改。
Go 用 const 关键字声明常量:
const Pi = 3.14159
const Name = "Go"
const MaxSize = 100
常量声明时必须赋值,赋值后不可修改:
const Pi = 3.14
Pi = 3.14159 // 报错:cannot assign to Pi
常量的类型
有类型常量
显式指定类型:
const Pi float64 = 3.14159
const Name string = "Go"
const MaxSize int = 100
无类型常量
不指定类型,让编译器根据上下文推断:
const Pi = 3.14159
const Name = "Go"
const MaxSize = 100
无类型常量有一个好处:它的精度更高,可以在需要时自动适配不同类型。
const Pi = 3.14159
var f32 float32 = Pi // 自动转为 float32
var f64 float64 = Pi // 自动转为 float64
如果 Pi 声明为 const Pi float64 = 3.14159,那它赋值给 float32 变量时就需要显式转换了。
NoteGo 的无类型常量拥有比任何具体类型都更高的精度。按规范,无类型整数常量和浮点常量都至少有 256 位精度。所以常量运算几乎不会溢出。
常量的值来源
常量的值只能来自:
- 字面量:
42、"hello"、true - 其他常量:
const B = A - 常量表达式:
const C = A + B - 结果是常量的类型转换:
const D = int(3.14)(但3.14本身就是无类型常量) - iota:枚举器
不能把函数返回值赋给常量:
const now = time.Now() // 报错:time.Now() 不是常量
常量只能是编译期就能确定的值。
批量声明
多个常量可以放在一起:
const (
StatusOK = 200
StatusError = 500
StatusNotFound = 404
)
省略值的情况
在同一个 const 分组里,如果某个常量没有赋值,它会复用上一个常量的值:
const (
A = 1
B // B = 1
C // C = 1
)
这个特性配合 iota 使用时特别有用。
iota 枚举器
iota 是 Go 的常量计数器,用来生成自增的枚举值。它在每个 const 分组里从 0 开始,每出现一行就加 1。
基本用法
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota 在 Sunday 那一行是 0,之后每行自动加 1。Monday 没有写表达式,复用了 iota,但此时 iota 的值是 1。
iota 的本质
iota 其实是行号的索引,从 0 开始计数:
const (
A = iota // iota = 0, A = 0
B // iota = 1, B = 1
C // iota = 2, C = 2
)
const (
X = iota // 新的 const 分组,iota 重置为 0, X = 0
Y // iota = 1, Y = 1
)
用表达式
iota 可以参与表达式运算:
const (
KB = 1 << (10 * (iota + 1)) // iota=0, 1 << 10 = 1024
MB // iota=1, 1 << 20 = 1048576
GB // iota=2, 1 << 30 = 1073741824
)
跳过值
用 _ 跳过某些值:
const (
_ = iota // 0,丢弃
KB = 1 << (10 * iota) // iota = 1, 1 << 10
MB // iota = 2, 1 << 20
GB // iota = 3, 1 << 30
)
位运算枚举
iota 特别适合做位标志(bitwise flags):
const (
Read = 1 << iota // 1 (二进制 0001)
Write // 2 (二进制 0010)
Execute // 4 (二进制 0100)
)
// 组合使用
perm := Read | Write // 3 (二进制 0011),表示可读可写
这种模式在权限控制、配置标志等场景非常常见。
自定义类型枚举
给常量指定一个自定义类型,让枚举更有语义:
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
func isWeekend(day Weekday) bool {
return day == Sunday || day == Saturday
}
这样 Weekday 就是一个独立的类型,不会和普通 int 混淆。
常量表达式
常量之间可以运算,结果是常量:
const (
Width = 100
Height = 200
Area = Width * Height // 20000
)
常量表达式的运算在编译期完成,不占用运行时资源。
Tip利用常量表达式,你可以把一些计算提前到编译期,减少运行时开销。比如
const BufferSize = 4 * 1024,编译器在编译时就确定了值是 4096。
无类型常量的妙处
来看一个无类型常量的实际好处:
const Factor = 3
var i int = Factor * 10 // 30, int
var f float64 = Factor * 1.5 // 4.5, float64
Factor 是无类型常量,它可以在 int 上下文中当 int 用,在 float64 上下文中当 float64 用。
如果写成 const Factor int = 3,第二行就会报类型不匹配,因为 int 乘 float64 在 Go 里不允许。
常见问题
常量不能取地址
const X = 10
ptr := &X // 报错:cannot take the address of X
常量没有内存地址,它只是一个编译期的值。只有变量才能取地址。
iota 不是关键字
iota 是预声明标识符,不是关键字。理论上你可以把它当变量名用(但千万别这么干):
func main() {
iota := 42 // 合法但极不推荐
fmt.Println(iota)
}
学完这节你能做什么
- 用
const声明常量 - 理解无类型常量为什么更灵活
- 用
iota生成自增枚举 - 用位运算 + iota 做权限标志
- 给枚举值指定自定义类型
下一节讲 fmt 包的输出和格式化。