首页 / Go 语言入门教程 / 常量与 iota

Go 语言入门教程

常量与 iota

本教程共 80 篇 · 第 9 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程const常量iota枚举无类型常量

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 变量时就需要显式转换了。

Note

Go 的无类型常量拥有比任何具体类型都更高的精度。按规范,无类型整数常量和浮点常量都至少有 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
)

iotaSunday 那一行是 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,第二行就会报类型不匹配,因为 intfloat64 在 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 包的输出和格式化。