Go 语言常量¶
常量是在编译期可表示且运行时不可重新赋值的值。 Go 常量可为布尔、数字、字符、字符串,也可以暂时保持“无类型”状态,从而获得灵活且精确的表达能力。
基本声明¶
const Pi = 3.141592653589793
const Greeting string = "hello"
const (
ReadTimeout = 5 * time.Second
WriteTimeout = 10 * time.Second
)
常量表达式不能调用普通运行时函数。
time.Second 本身是常量,因此上面的持续时间声明合法。
无类型常量¶
未显式指定类型的常量可能是无类型布尔、整数、浮点、复数、字符或字符串。 无类型常量在赋给变量或传入参数时,才根据上下文选择可表示的具体类型。
只要值可表示,同一个常量可以用于不同数字类型。
常量精度和表示范围¶
数字常量可具有高于运行时基础类型的精度:
Huge 可以参与常量计算,但不能赋给无法容纳它的运行时整数类型:
编译期精确不等于转换到 float64 后仍保持十进制精确。
iota¶
iota 在每个 const 声明组中从 0 开始,并按 ConstSpec 递增:
省略后续表达式时,会重复前一个非空表达式及其类型。
位标志示例¶
type Permission uint8
const (
PermissionRead Permission = 1 << iota
PermissionWrite
PermissionExecute
)
func hasPermission(all, target Permission) bool {
return all&target != 0
}
位标志适合可组合选项,但需要明确零值、未知位和序列化兼容策略。
差异示例一:常量与变量¶
端口字面值可在编译期确定,因此可为常量。 当前时间必须在运行时调用函数获得,因此只能是变量。 常量不是“绝对不变配置”的同义词;部署配置通常仍来自环境或文件。
差异示例二:无类型常量与有类型常量¶
无类型常量可根据目标上下文表示为 int64;有类型常量遵循普通类型兼容规则。
公开 API 中显式类型能表达领域含义,但也会减少使用时的灵活性。
差异示例三:iota 与显式协议值¶
iota 适合内部连续枚举或位标志。
外部协议、数据库状态和持久化格式应显式写值,因为在声明组中插入一行可能改变后续 iota 数值。
常量命名边界¶
Go 不要求常量使用全大写蛇形命名。
常量遵循普通 Go 命名方式,例如 MaxPacketSize;是否导出仍由首字母大小写决定。
名称中应避免重复包语境,例如 http.HTTPDefaultPort 通常不如 http.DefaultPort 简洁。
常见错误¶
- 尝试将切片、映射、结构体或函数调用结果声明为常量。
- 认为常量一定有
int或float64类型,忽略无类型常量。 - 用
iota表示外部持久化值,后续插入常量破坏兼容。 - 把环境可变配置硬编码为常量。
- 将很大的无类型常量转换为较小整数而触发编译溢出。
- 使用常量模拟缺少验证的“枚举”,却允许任意底层值进入 API。
工程实践¶
- 使用定义类型配合常量表达领域集合,并提供验证或
String方法。 - 外部协议值全部显式固定,新增值不修改既有编码。
- 单位写入名称或使用
time.Duration等有语义类型。 - 位标志预留未知位处理策略,反序列化时验证非法组合。
- 常量组按同一概念组织,不创建包含无关值的巨大
const块。 - 对公开常量写清稳定性和可组合规则。
Go 常量的强项是无类型精确计算和清晰的编译期约束,而不是替代运行时配置系统。