
`cap(ch)` 对 channel 的调用不是常量表达式,而是在运行时动态求值的;因为 channel 的容量由 `make` 在运行时决定,类型系统不包含该信息,故无法在编译期确定。
在 Go 语言中,len() 和 cap() 是内置函数,但它们是否为常量表达式(constant expression),取决于操作数的类型和上下文。根据 Go 语言规范:
len(s) 和 cap(s) 是常量,当且仅当 s 的类型是数组或指向数组的指针,且 s 不包含 channel 接收操作或非常量函数调用;此时 s 不会被求值。 对于字符串字面量,len(s) 是常量;但对于 slice、map、channel 或 pointer to slice 等,len/cap 均为运行时求值的表达式,且操作数会被实际计算。
以 channel 为例:
ch := make(chan int, 10) fmt.Println(cap(ch)) // ✅ 合法,输出 10 —— 但这是运行时计算结果
虽然此处 cap(ch) 返回 10,但它不是编译期常量。你无法将其用于需要常量的上下文,例如:
const c = cap(ch) // ❌ 编译错误:cap(ch) is not a constant var arr [cap(ch)]int // ❌ 错误:数组长度必须是常量
这是因为:
- channel 类型(如 chan int)本身不携带容量信息 —— 容量仅存在于运行时由 make 分配的底层结构中;
- cap(ch) 实际调用的是运行时函数 runtime.chancap(),需访问 channel 的内部字段(如 qcount, dataqsiz),显然依赖运行时状态;
- 即使 ch 是包级变量或局部常量绑定(如 const ch = make(chan int, 5)),Go 也不允许 make 出现在常量表达式中 —— make 本身就是运行时操作。
✅ 正确理解:
cap(ch) 是一个纯函数式运行时查询,无副作用,但必须执行、不可省略。它与 len(slice) 类似(后者也非编译时常量),而不同于 len([5]int{})(数组长度是类型固有属性,编译期已知)。
? 小结:
- ✅ cap(ch) 合法、安全、常用,适用于运行时容量检查;
- ❌ 不能用于常量定义、数组长度、switch case 值等要求编译期常量的场景;
- ? 若需编译期已知容量,应改用数组或带长度的结构体字段,而非 channel。
理解 len/cap 的常量性边界,有助于写出更符合 Go 类型系统特性的健壮代码。










