2.2 控制流:if、for 与 switch


2.2 控制流:if、for 与 switch

本节摘要:Go 的控制流关键字少到极致:没有 while、没有 do-while,循环只有 for 一个入口,但它身兼三职;if 与 switch 都支持初始化子句,把"临时变量的作用域压进判断语句"变成惯用法;switch 的 case 自带 break,支持无表达式的开关链写法。本节逐个拆解这些形态并给出选型标准。

先说结论

阅读完本节,你应当能够:

  1. 用 for 写出经典式、条件式与无限循环三种形态;
  2. 用 for range 遍历切片、map 与字符串并注意取值语义;
  3. 用初始化子句收窄 if 与 switch 中临时变量的作用域;
  4. 解释 switch 的隐式 break 与显式 fallthrough 的边界;
  5. 在 for range 中安全地取地址与跳出外层循环。

一、for 一人分饰三角

// 形态一:经典三段式 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:初始化子句是灵魂

if err := doSomething(); err != nil { return err } // 这里已经访问不到 err

分号前是只在本判断内活着的临时变量。这个写法是 Go 错误处理的标点符号——第 4 章会看到它铺满全屏。另一个硬规矩:条件不能加括号,且左花括号必须与 if 同行,写惯 C 的人第一周会浑身难受,第二周开始享受没有风格之争的世界。

三、switch:两种形态都很能打

表达式形态,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 与标签

单层循环里 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% 场景。

本节速览

  • for 三加一形态:三段式、条件式、无限式,外加 range 遍历。
  • range 语义:切片给下标与拷贝值,map 顺序随机,字符串按 rune 解码,channel 收到关闭为止。
  • 迭代变量陷阱:1.22 前后语义不同,取地址需谨慎。
  • if 初始化子句:临时变量作用域压进判断语句,是错误处理惯用法的骨架。
  • switch 隐式 break:穿透须显式 fallthrough;无表达式形态是开关链的最佳载体。
  • 标签跳出:break 加标签可离开多层循环,但更推荐抽函数。

下一节把这些流程打包成函数,并请出 defer 与 panic。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U