常量与 iota
问题
Go 的常量有什么特点?iota 如何使用?如何用 iota 实现枚举?
答案
常量基础
Go 常量使用 const 声明,必须在编译期确定值:
const Pi = 3.14159
const MaxSize int = 1024
// 批量声明
const (
StatusOK = 200
StatusCreated = 201
StatusBadRequest = 400
)
无类型常量
Go 常量默认是无类型的(untyped),可以自由参与不同类型的运算:
const x = 10 // 无类型整数常量
var a int32 = x // OK
var b float64 = x // OK
var c int64 = x // OK
const y int = 10 // 有类型常量(int 类型)
// var d int32 = y // 编译错误!int 不能赋给 int32
无类型常量的精度远超具体类型——Go 的无类型整数常量至少有 256 位精度。
iota 枚举器
iota 是 Go 的常量生成器,在 const 块中从 0 开始递增:
const (
Sunday = iota // 0
Monday // 1(隐式 = iota)
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota 高级用法
// 跳过某个值
const (
_ = iota // 0(跳过)
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30
TB // 1 << 40
)
// 位运算枚举(权限控制)
type Permission uint8
const (
Read Permission = 1 << iota // 001 = 1
Write // 010 = 2
Execute // 100 = 4
)
// 使用位运算组合和检查权限
perm := Read | Write // 011 = 3
hasRead := perm & Read != 0 // true
hasExec := perm & Execute != 0 // false
// 实现 String() 方法
func (p Permission) String() string {
var perms []string
if p&Read != 0 { perms = append(perms, "r") }
if p&Write != 0 { perms = append(perms, "w") }
if p&Execute != 0 { perms = append(perms, "x") }
return strings.Join(perms, "")
}
fmt.Println(Read | Write) // "rw"
iota 的作用域规则
// 每个 const 块重新从 0 开始
const (
A = iota // 0
B // 1
)
const (
C = iota // 0(重新开始!)
D // 1
)
// 同一行的 iota 值相同
const (
X, Y = iota, iota + 10 // X=0, Y=10
Z, W // Z=1, W=11
)
常见面试问题
Q1: const 和 var 定义的区别?
答案:
| 维度 | const | var |
|---|---|---|
| 值确定时机 | 编译期 | 运行期 |
| 可否修改 | ❌ | ✅ |
| 可否用函数返回值 | ❌ | ✅ |
| 类型 | 可以无类型 | 必须有类型 |
| 存储 | 编译进程序 | 内存中 |
Q2: iota 在中间插入一个非 iota 值会怎样?
答案:
iota 不会受到影响,它始终按自己的计数器递增:
const (
A = iota // 0
B = 100 // 100(显式赋值,iota 仍然是 1)
C = iota // 2(iota 继续递增)
D // 3
)
Q3: 如何实现类似其他语言的枚举验证?
答案:
Go 没有内置枚举验证,但可以自己实现:
type Color int
const (
Red Color = iota
Green
Blue
colorEnd // 私有,作为边界
)
func (c Color) IsValid() bool {
return c >= Red && c < colorEnd
}
func ParseColor(s string) (Color, error) {
switch strings.ToLower(s) {
case "red": return Red, nil
case "green": return Green, nil
case "blue": return Blue, nil
default: return 0, fmt.Errorf("invalid color: %s", s)
}
}