Constants(常量)
常量(constant)表示编译期即可确定、运行期间不会改变的值。Go 常量只能是布尔值、数值、字符串等常量值,不能保存切片、映射、函数调用结果或需要运行时分配的数据。
声明常量
使用 const 声明单个常量或常量组:
package main
import "fmt"
const AppName = "study-service"
const MaxRetries int = 3
const (
Development = "development"
Production = "production"
)
func main() {
fmt.Println(AppName, MaxRetries, Development, Production)
}
常量可以声明在包级或函数内部。它必须在声明时得到值,之后不能重新赋值。
常量表达式
常量值可以由字面量、其他常量和允许用于常量求值的操作组成:
const SecondsPerMinute = 60
const MinutesPerHour = 60
const SecondsPerHour = SecondsPerMinute * MinutesPerHour
const Banner = "Go" + " constants"
普通函数调用通常在运行时执行,因此不能用于初始化常量:
// const StartedAt = time.Now() // 编译错误:time.Now() 不是常量表达式
需要运行时计算的固定配置可以使用包级变量,但此时它仍是变量,而不是语言意义上的常量。
有类型与无类型常量
显式写出类型的常量称为有类型常量;未写类型的常量通常保持为无类型常量,直到上下文要求具体类型:
package main
import "fmt"
const UntypedLimit = 100
const TypedLimit int64 = 100
func main() {
var count int = UntypedLimit
var size int64 = UntypedLimit
fmt.Printf("%T %T\n", count, size)
}
无类型常量可以在其值可表示的前提下用于不同的兼容数值类型。这种灵活性不代表变量之间也能隐式转换:
var count int = 100
// var size int64 = count // 编译错误:变量 count 不会隐式转换为 int64
var size int64 = int64(count)
当无类型常量进入需要具体类型的上下文时,编译器会检查该值是否可由目标类型表示。超出范围会在编译期报错。
数值常量的精度
数值常量可以保持比机器基础类型更高的精度,直到被赋给具体类型或参与需要具体类型的运算:
const Large = 1 << 100
// var value uint64 = Large // 编译错误:Large 超出 uint64 范围
这适合表达精确的编译期计算,但不能据此认为运行时的 int、float64 等类型也拥有任意精度。
使用 iota 生成连续常量
在常量组中,iota 从 0 开始,并在每个 ConstSpec 后递增。它常用于生成一组相关整数:
package main
import "fmt"
type Status int
const (
StatusUnknown Status = iota
StatusPending
StatusRunning
StatusDone
)
func main() {
fmt.Println(StatusUnknown, StatusPending, StatusRunning, StatusDone)
}
输出如下:
0 1 2 3
常量组省略后续表达式时,会重复上一项的类型和表达式,因此上例中的每一项仍会重新计算当前 iota。
iota 也适合生成位标志:
type Permission uint
const (
PermissionRead Permission = 1 << iota
PermissionWrite
PermissionDelete
)
不要把 iota 当成严格的枚举类型系统。Status 的底层类型仍是整数,显式转换后可以产生常量列表之外的值,因此接收外部输入时仍要校验。
跳过值与稳定性
空白标识符可以跳过某个序号:
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
)
如果常量值会被写入数据库、日志、网络协议或持久化文件,不应随意在常量组中间插入新项,因为后续 iota 值会整体变化。对外部协议应显式指定稳定数值,并为未知值保留处理路径。
常见错误
- 试图用
const保存time.Now()、环境变量或函数返回值;这些值要到运行时才能获得。 - 认为无类型常量没有类型规则;它仍有默认类型,并且进入具体上下文时必须可表示。
- 把
iota生成的整数集合当成封闭枚举,忘记校验列表之外的值。 - 依赖
iota序号对外持久化,却在常量组中间插入新项导致兼容性破坏。 - 为了模拟其他语言风格而把常量全部写成下划线大写;Go 名称通常使用驼峰形式,并由首字母决定是否导出。
与 TypeScript 的关键差异
TypeScript 的 const 约束变量绑定不能重新赋值,但对象内容仍可能被修改;Go 的 const 表示编译期常量值,并且不能用于结构体、切片或映射。TypeScript enum 也不等同于 Go 的命名整数类型加常量组:Go 不会自动限制变量只能取已声明常量。
命名类型、底层类型和可赋值性边界见 Type(类型)。
小结
const只表示编译期可确定的布尔、数值、字符串等常量值。- 无类型常量在进入具体上下文前保持较高精度和赋值灵活性。
- 变量不会因为值相同而获得无类型常量的隐式转换能力。
iota在常量组中逐项递增,适合内部序号和位标志。- 对外持久化或传输的数值应保持稳定,并校验未知值。