Go 的 const 是编译期固化、无内存地址的真常量,必须在编译时确定值,不支持运行时函数调用或变量依赖;iota 是 const 块内重置的行计数器;无类型常量更灵活,显式类型增强安全约束。

Go 里的 const 不是“运行时只读”,而是“根本没地址、编译期就固化”——你连取地址 &Pi 都会报错,这才是它真正防篡改的底层逻辑。
常量必须编译期确定,函数调用、len、make 全都不行
很多人写 const buf = make([]byte, 1024) 或 const port = os.Getenv("PORT"),结果构建直接失败,错误是:constant initializer must be a compile-time constant。这是因为 Go 要求所有 const 值在编译时就能算出结果,不依赖任何运行时环境。
- ✅ 正确:
const Pi = 3.14159、const MaxRetries = 3 * 2、const IPv4Len = len("192.168.0.1")(len作用于字符串字面量时是编译期常量) - ❌ 错误:
const now = time.Now()、const s = strings.ToUpper("hello")、const n = len(mySlice) - ⚠️ 注意:
len("abc")合法,但len([]byte{"a","b","c"})不合法——后者涉及运行时构造切片
iota 是 const 块专属计数器,跨块重置、同行共享
iota 看似简单,但新手常栽在“为什么我的枚举从 0 变成 100?”或者“Red, Green, Blue 全是 0?”这类问题上。它不是全局变量,而是一个编译器内置的、仅在 const 块内生效的行号计数器。
- 每进入一个新
const (...)块,iota自动重置为 0 - 同一行多个常量共用同一个
iota值(如A, B = iota, iota→ A=0, B=0) - 跳过某值要显式处理:
_ = iota(丢弃)、Unused = iota + 1(偏移)、或直接写死Reserved = 99 - 示例:
const ( First = iota // 0 Second // 1 _ // 2,被丢弃 Fourth // 3 )
无类型常量更灵活,显式类型更适合强约束场景
Go 的常量默认是“无类型”的(untyped),比如 const timeout = 5,它既可赋给 int,也能直接传给 time.Sleep(5 * time.Second);但如果你定义成 const timeout int = 5,它就只能当 int 用,不能直接参与 time.Duration 运算——这既是限制,也是类型安全的保障。
立即学习“go语言免费学习笔记(深入)”;
- ✅ 推荐无类型:数学常数、端口号、状态码等通用值(
const Port = 8080) - ✅ 推荐显式类型:需要和特定类型强绑定时,如
const Timeout time.Duration = 30 * time.Second,避免隐式转换歧义 - ⚠️ 混淆点:批量声明中若第一个带类型,后续省略类型则自动继承该类型(
const (A int = 1; B)→B也是int)
常量不能用于变量声明的“右侧初始化表达式”?其实是能的,但有边界
有人以为 const Max = 100; var limit = Max 会报错,其实完全合法——常量可以参与变量初始化,只要右边表达式本身是合法的。真正卡住的是“常量表达式中嵌套变量”:
- ✅ 合法:
const Max = 100; var x = Max * 2(Max是编译期常量,乘法仍是常量表达式) - ✅ 合法:
const Mode = "debug"; var cfg = Config{Env: Mode}(字符串常量赋值给 struct 字段) - ❌ 非法:
var v = 42; const c = v + 1(v是变量,无法参与常量计算) - ⚠️ 关键区别:常量能“用”,但不能“依赖变量”;变量能“用常量”,但不能“让常量反过来依赖自己”
最容易被忽略的一点:常量的“不可变性”不是靠运行时保护,而是靠编译器彻底剥离了它的内存存在——它没有地址、不占运行时空间、甚至不会出现在二进制符号表里(除非导出)。这意味着,哪怕你用 unsafe 也改不了它。这种设计不是为了炫技,而是为了让数组长度、switch 分支、泛型约束这些关键机制获得绝对确定性。










