Conditional Control(条件控制)
Go 使用 if 和 switch 控制条件分支。条件必须产生 bool,不能把数字、字符串、指针或集合当作隐式真假值。Go 没有三元条件运算符,分支逻辑应通过明确的控制流表达。
基本 if 语句
if 条件不需要圆括号,代码块的花括号不可省略:
package main
import "fmt"
func main() {
age := 20
if age >= 18 {
fmt.Println("adult")
}
}
条件必须是布尔表达式:
count := 1
if count > 0 {
fmt.Println("has items")
}
// if count { } // 编译错误:count 不是 bool
这条规则避免了 0、空字符串和 nil 的隐式真值转换。
else if 与 else
多个互斥条件可以组成分支链:
package main
import "fmt"
func main() {
score := 82
if score >= 90 {
fmt.Println("excellent")
} else if score >= 60 {
fmt.Println("passed")
} else {
fmt.Println("failed")
}
}
Go 的分号插入规则要求 else 与前一个右花括号位于同一行。标准格式应写成 } else {,并交给 gofmt 统一排版。
条件顺序会影响结果。范围判断通常从更严格的条件开始,避免较宽条件提前匹配。条件来自外部输入时,还应先验证输入范围。
带初始化语句的 if
if 可以在条件前执行一条简单语句,两者使用分号分隔:
package main
import (
"fmt"
"strconv"
)
func main() {
if value, err := strconv.Atoi("42"); err != nil {
fmt.Println("invalid number:", err)
} else {
fmt.Println(value * 2)
}
}
初始化语句声明的变量只在整个 if、else if 和 else 结构中可见。把临时结果和错误限制在最小作用域内,可以减少误用;如果后续代码仍需要该值,就应在 if 之前声明。
提前返回
处理错误和无效输入时,提前返回通常能让正常路径保持平直:
package main
import (
"errors"
"fmt"
)
func priceWithDiscount(price float64, member bool) (float64, error) {
if price < 0 {
return 0, errors.New("price must not be negative")
}
if !member {
return price, nil
}
return price * 0.9, nil
}
func main() {
price, err := priceWithDiscount(100, true)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(price)
}
当一个分支已经 return、break、continue 或 panic 后,通常不需要再用 else 包住后续正常路径。提前返回不是机械规则;出口过多导致资源清理或状态变化难以追踪时,应重新划分函数职责。
表达式 switch
switch 适合对一个值进行多个互斥匹配:
package main
import "fmt"
func main() {
method := "POST"
switch method {
case "GET":
fmt.Println("read")
case "POST", "PUT":
fmt.Println("write")
default:
fmt.Println("unsupported")
}
}
每个 case 可以包含多个逗号分隔的表达式。Go 的 switch 默认不会继续执行下一个 case,因此通常不写 break。default 可放在任意位置,但通常放在最后便于阅读。
无表达式 switch
省略表达式时,switch 等价于对 true 进行匹配,可以替代较长的 if else 链:
package main
import "fmt"
func main() {
temperature := 31
switch {
case temperature >= 35:
fmt.Println("hot")
case temperature >= 20:
fmt.Println("warm")
default:
fmt.Println("cool")
}
}
分支从上到下求值,只执行第一个匹配的 case。因此条件有包含关系时,仍应把更严格的条件放在前面。
switch 也可以包含初始化语句:
switch value := loadStatus(); value {
case "ready":
fmt.Println("start")
default:
fmt.Println("wait")
}
value 的作用域覆盖整个 switch。
fallthrough
fallthrough 会无条件进入下一个 case 的语句体,不会重新检查下一个 case 的条件:
switch level {
case "debug":
fmt.Println("debug details")
fallthrough
case "info":
fmt.Println("general message")
}
它只能作为非最后一个表达式 switch 分支的最后一条非空语句使用。大多数逻辑可以通过合并 case、提取函数或显式调用表达得更清楚,应谨慎使用。
类型 switch
类型分支用于检查接口值保存的动态类型:
package main
import "fmt"
func describe(value any) {
switch current := value.(type) {
case int:
fmt.Println("int:", current)
case string:
fmt.Println("string:", current)
case nil:
fmt.Println("nil")
default:
fmt.Printf("other: %T\n", current)
}
}
func main() {
describe(42)
describe("Go")
}
value.(type) 只能出现在类型 switch 的守卫中。分支变量 current 在单一具体类型的 case 中具有该具体类型;在包含多个类型的 case 或 default 中保持接口类型。类型分支适合处理确实允许多种动态类型的边界,不应替代清晰的接口方法设计。
接口值、typed nil 和普通类型断言的完整规则见 Interface(接口)。
常见错误
- 把整数、字符串或
nil直接写成if条件;Go 不做隐式真值转换。 - 省略花括号,或把
else放到前一个右花括号的下一行。 - 忘记初始化语句中的变量只在当前条件结构内可见。
- 按照 JavaScript 习惯为每个
case添加break,或误以为 Go 默认贯穿后续分支。 - 滥用
fallthrough,并错误地认为下一个case条件还会被检查。 - 使用很长的类型
switch代替由接口表达的稳定行为契约。
与 TypeScript/JavaScript 的关键差异
- Go 条件必须是
bool,没有 JavaScript 的 truthy/falsy 规则。 - Go 没有
?:三元条件运算符,也没有通过&&或||返回任意操作数的用法。 - Go 的
switch默认不贯穿下一个分支,JavaScript 通常需要显式break才能阻止贯穿。 - Go 的
if和switch可以带初始化语句,并把变量作用域限制在当前控制结构。 - 类型
switch检查接口值的动态类型,不等同于 TypeScript 编译期的联合类型收窄。
小结
if条件必须是bool,条件外不写圆括号,代码块花括号不可省略。- 初始化语句可以把临时值和错误限制在当前
if或switch中。 - 提前返回适合处理错误和边界条件,使正常路径减少嵌套。
switch默认只执行第一个匹配分支,不会自动贯穿。- 无表达式
switch适合组织互斥布尔条件,条件顺序仍然重要。 - 类型
switch用于有限的动态类型分派,不应取代清晰的接口设计。