本节摘要:Go 的控制流关键字少到极致:没有 while、没有 do-while,循环只有 for 一个入口,但它身兼三职;if 与 switch 都支持初始化子句,把"临时变量的作用域压进判断语句"变成惯用法;switch 的 case 自带 break,支持无表达式的开关链写法。本节逐个拆解这些形态并给出选型标准。
阅读完本节,你应当能够:
// 形态一:经典三段式 for i := 0; i < 5; i++ { fmt.Println(i) } // 形态二:只有条件,就是 while n := 1 for n < 100 { n *= 2 } // 形态三:什么都没有,无限循环 for { // 用 break 或 return 离开 }
三段式里各段都可省:省前后留中间就是 while;全省就是死循环,伺服主循环、消息泵都是这个形态,配合第 5 章的 channel 退出信号使用。
for range 是第四种、也是日常最高频的形态:
nums := []int{10, 20, 30} for i, v := range nums { fmt.Println(i, v) }
遍历 map 时产出键值对(顺序刻意随机化,防止程序依赖遍历顺序);遍历字符串时按 rune 解码;遍历 channel 时持续接收直到通道关闭。不需要下标就用下划线丢弃。
⚠️ 常见坑(高频面试题):range 循环里对元素取地址存进切片,最终所有指针指向同一个变量——Go 1.22 之前每次迭代复用同一个循环变量。升级到 1.22 及以后,每次迭代是新变量,老代码反而"意外修好"。写跨版本代码时,用局部拷贝最稳妥。
if err := doSomething(); err != nil { return err } // 这里已经访问不到 err
分号前是只在本判断内活着的临时变量。这个写法是 Go 错误处理的标点符号——第 4 章会看到它铺满全屏。另一个硬规矩:条件不能加括号,且左花括号必须与 if 同行,写惯 C 的人第一周会浑身难受,第二周开始享受没有风格之争的世界。
表达式形态,case 自动 break,不穿透:
switch x { case 10: fmt.Println("x 是 10") default: fmt.Println("x 不是 10") }
想穿透到下一个 case 得显式写 fallthrough——把默认安全、显式危险,方向和 C 相反。case 还可以一次列多个值(逗号分隔),甚至放表达式。
无表达式形态,写成清晰的开关链,替代 if-else 山:
switch { case score >= 90: grade = "A" case score >= 60: grade = "B" default: grade = "C" }
我个人的分界线:比较同一个变量的多个值用表达式形态;多个独立布尔条件用无表达式形态;超过三层的 if-else 一律重构成 switch 或表驱动。

单层循环里 break 跳出、continue 进下一轮,无悬念。嵌套循环要"从内层直接离开外层"时用标签:
outer: for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { if i*j > 3 { break outer // 直接离开两层 } } }
标签写在循环前面加冒号。这个特性用得很少,但看老代码时得认识。更常见的重构思路是把内层抽成函数用 return 离开。
| 需求 | 首选写法 | 备注 |
|---|---|---|
| 已知次数的循环 | for 三段式 | 下标局部于循环头 |
| 条件驱动 | for 单条件 | 就是 while |
| 主循环/事件泵 | for 无限式 | 配 channel 退出 |
| 遍历容器/串 | for range | map 顺序随机 |
| 同变量多值分支 | switch 表达式 | case 隐式 break |
| 多布尔条件分支 | switch 无表达式 | 替代 if-else 链 |
| 类型分支 | switch 类型断言 | 见第 3 章 |
💡 关键直觉:控制流越少,代码越像表格。当你发现自己在数 else 的层数,就该改成 switch 或把条件查表了。
补几个实用细节。for range 的下标与值都是拷贝:遍历结构体切片时改 v 的字段不会写回原切片,要改就 s[i].Field = ... 用下标。遍历时删除元素:正向边遍历边删会跳元素,惯用法是"原地压缩"——用一个写指针只保留要留的元素,一遍扫完重新切长度。switch 的 case 求值顺序:从上到下,命中即止(除非 fallthrough),把高频分支放前面是零成本优化。goto 还在吗? 在,但几乎只活在自动生成代码里;人写的代码用标签加 break、continue 或抽函数覆盖 99% 场景。
下一节把这些流程打包成函数,并请出 defer 与 panic。