Go 常量与 iota 枚举

常量(constant)是一种"在程序运行期间不能改变的值"。它和变量的最大区别是:变量用 var 声明且可以反复修改,常量用 const 声明且一经定义就不可重新赋值。常量常用于圆周率、配置项、错误码、枚举值等不希望被误改的场景。

一、const 声明常量

声明常量和声明变量很像,把 var 换成 const 即可。多个相关的常量推荐用 const ( ... ) 分组放在一起,看起来更整齐。

package main

import "fmt"

// 单个常量声明
const PI = 3.14159
const Max int = 100

// 分组声明(推荐写法)
const (
    Name = "Go"
    Year = 2009
)

func main() {
    fmt.Println(PI, Max, Name, Year)
    // PI = 3 // 编译错误:不能给常量重新赋值
}

注意:常量一旦定义就不能再赋值,否则编译直接报错。这个检查发生在编译期,不会拖慢运行速度。常量也必须能在编译期求值,所以你不能把函数的返回值(运行时才有结果)赋给常量。

二、iota:常量计数器

一个个手写常量的值太麻烦,Go 提供了一个特殊的常量计数器 iota。它的规则简单记住三点:

下面这个星期枚举是最经典的入门例子:

package main

import "fmt"

// iota:常量计数器,在 const 块中从 0 开始,每新增一行自动 +1
const (
    Sunday    = iota // 0
    Monday           // 1(省略表达式时,自动复用上一行)
    Tuesday          // 2
    Wednesday        // 3
    Thursday         // 4
    Friday           // 5
    Saturday         // 6
)

func main() {
    fmt.Println(Sunday, Monday, Saturday) // 0 1 6
}

周一到周六都没有写 = 某值,但它们分别得到 1~6,就是因为"复用上一行表达式 iota"再加上 iota 每行自增。这比手写 0、1、2、3…要安全得多,你随时插一行进去也不必重新排编号。

三、位标志:权限模型

iota 配合位左移 << 可以做出非常优雅的标志位(flag)。Linux 文件权限里的"可读、可写、可执行"就是这种思路:

package main

import "fmt"

// 用 iota + 位左移实现权限标志位
const (
    Read    = 1 << iota // 1 << 0 = 1
    Write               // 1 << 1 = 2
    Execute             // 1 << 2 = 4
)

func main() {
    // 位或组合:可读 + 可写
    perm := Read | Write
    fmt.Println(perm) // 3

    // 位与检测:是否拥有读权限
    fmt.Println(perm&Read != 0) // true
}

三个权限分别是 1、2、4(都是 2 的幂),用位或 | 组合起来,之后用位与 & 就能快速判断是否包含某个权限。这种"位掩码"在系统编程、配置开关、状态标记里非常常见,效率高、占用小。

四、文件大小单位

另一个高频用法是定义 KB、MB、GB 这种 1024 进制的单位。这里的小技巧是把 iota 放进表达式 10 * iota 里,让位移量随行递增,后续几行省略表达式即可自动套用:

package main

import "fmt"

// 经典用法:文件大小单位(2 的 10/20/30/40 次方)
const (
    KB = 1 << (10 * iota) // 1 << 10
    MB                    // 1 << 20
    GB                    // 1 << 30
    TB                    // 1 << 40
)

func main() {
    fmt.Println(KB) // 1024
    fmt.Println(MB) // 1048576
    fmt.Println(GB) // 1073741824
}

五、无类型常量

Go 的常量还有一个特别之处:无类型常量(untyped constant)。当你写 const PI = 3.14159 而不指定类型时,它并不是 float64,而是一个"数学意义上的高精度数"。它在参与运算时,会根据上下文临时确定类型,因此可以写出 var x = PI * 10(整数 10 也能直接相乘)而不报错——这若发生在变量之间是不允许的。

无类型常量默认拥有很高的精度(至少 256 位),非常适合做数学计算。一旦它被赋值给一个具体类型的变量,或用在需要明确类型的上下文里,才会"定型"。这也是 Go 对数值常量的一种贴心设计。

小结

常量用 const,不可变;iota 是 const 块里从 0 开始的行计数器,适合做枚举和位标志;省略表达式会自动复用上一行;不写类型的常量是无类型常量,精度更高、更灵活。下一篇我们来看 Go 的运算符

上一篇 Go 变量 | 下一篇 Go 运算符

✈️💬