switch 把一个值与一组 case 依次比较,执行第一个匹配的 case。和 C、Java、JavaScript 不同,Go 的 case 不会贯穿到下一个,所以不存在忘写 break 的问题。
case 从上到下依次检查,只有第一个匹配的 case 会执行。没有任何匹配时执行 default;它是可选的,可以出现在列表中的任何位置,不过按惯例放在最后。
一个 case 匹配多个值
case "sat", "sun": 匹配其中任意一个值。C 语言里用堆叠的空 case 实现的效果,Go 就是这样表达的。任何可比较的类型都可以:字符串、数字、rune、布尔值、指针,以及枚举之类的具名类型:
两个 case 的常量值相同会导致编译错误:
./main.go:10:7: duplicate case 1 (constant of type int) in expression switch
./main.go:8:7: previous case
没有条件的 switch
省略表达式后,每个 case 都变成一个布尔判断。这是 Go 中比长长的 if/else if 链更简洁的写法:
switch { 是 switch true { 的简写。因为第一个为真的 case 胜出,所以顺序很重要:只有 bmi < 18.5 为假时才会走到 bmi < 25,因此每个 case 都可以假定上面的 case 都不成立。
带初始化语句的 switch
和 if 一样,switch 可以以一条简短语句开头。在那里声明的变量,作用域仅限于这个 switch:
初始化语句也可以和无条件 switch 结合使用:switch n := len(items); { case n == 0: ... }。
fallthrough
如果确实想继续执行下一个 case,就在 case 的最后一条语句写 fallthrough。它会把控制权转到下一个 case 的代码体,而不计算它的条件:
等级 2 会打印 write,然后打印 read,权限向下逐级累加。后面 case 的条件不会被检查:从 case 2 执行 fallthrough 之后,即使 level 是 2,case 1 也会执行。
fallthrough 的规则:
- 它必须是 case 中的最后一条语句。
- 它不能出现在最后一个 case 中:
cannot fallthrough final case in switch。 - 类型 switch 中不允许使用它。
实际上,Go 代码里很少用到 fallthrough。一个 case 匹配多个值已经满足了常见需求,而显式的函数调用通常也比一串 fallthrough 更清楚。
switch 中的 break
case 里的 break 会退出 switch。case 会自行结束,所以只有想提前离开某个 case 时才需要它。陷阱在于循环里的 switch:
没有 loop 标签时,break 只结束 switch,循环会继续处理 "c"。而 continue 总是指向外层的循环,因为 switch 不是循环。
按类型 switch
类型 switch 根据接口值的动态类型进行分支,使用特殊形式 v.(type):
在只有一个类型的 case 中,x 就是那个类型(在 string case 中可以用 len(x))。在列出多个类型的 case 中,x 仍然是接口类型。类型断言一节会详细讲解类型 switch 和 v.(T) 断言。
Go 的 switch 与其他语言的不同之处
| 行为 | C、Java、JavaScript | Go |
|---|---|---|
| 贯穿到下一个 case | 默认如此 | 只有用 fallthrough 时 |
每个 case 末尾的 break | 必须写 | 不需要 |
| 一个 case 多个值 | 堆叠空 case | case a, b, c: |
| case 的值 | (大多是)常量 | 任意表达式,从上到下求值 |
| switch 的值 | 整数类型(C) | 任意可比较类型 |
| 没有 switch 表达式 | 不允许 | switch { case cond: } |
case 表达式不必是常量。case limit(), max + 1: 是合法的;它们按顺序求值,直到有一个匹配,所以后面的表达式可能永远不会执行。
穷尽性检查
Go 不会检查针对枚举的 switch 是否覆盖了所有值。可以加一个 default,对意料之外的值记录日志、返回错误或 panic,或者使用 exhaustive 检查工具(属于 golangci-lint),在构建时报告缺失的 case。
常见问题
Go 的 switch 会贯穿到下一个 case 吗?
不会。Go 的 case 在遇到下一个 case 时自动结束;你永远不需要写 break 来终止它。如果想让执行继续进入下一个 case 的代码体,就在该 case 的最后一条语句写 fallthrough。它会无条件跳转,不检查下一个 case 的条件。
Go 中如何在一个 case 里匹配多个值?
用逗号分隔列出它们:case "sat", "sun":。只要 switch 的值等于其中任意一个,这个 case 就会执行。这代替了 C 或 Java 中层层堆叠的空 case。
Go 中没有条件的 switch 是什么?
switch { case x < 0: ...; case x == 0: ...; default: ... }。switch 后面没有表达式时,每个 case 都是一个布尔条件,第一个为真的 case 会执行。这是替代长长的 if/else if 链的惯用写法。
Go 中 switch 里的 break 有什么作用?
它结束的是 switch,而不是外层的 for 循环。因为 case 会自行结束,所以很少需要它,但在循环里的 switch 中写 break、想要跳出循环的人常会因此踩坑。要跳出循环,请用带标签的 break 或 return。