Go 的 if 条件两边不加圆括号,花括号必不可少,而且可以先写一条简短语句,声明只在这个 if 内有效的变量。
条件从上往下依次检查,执行第一个为真的分支。else if 和 else 都是可选的。
语法规则
有三条规则与 C、Java 和 JavaScript 不同:
- 条件两边不加圆括号。
if (x > 5)能编译通过,但gofmt会去掉这对圆括号。 - 花括号必不可少,哪怕只有一条语句。不存在不带花括号的
if x > 5 return。 else要和右花括号写在同一行。Go 会在行尾的}后插入分号,所以下面的写法会失败:
if x > 5 {
fmt.Println("big")
}
else {
fmt.Println("small")
}
syntax error: unexpected keyword else, expected }
把 } else { 写在同一行。
条件必须是 bool 类型。Go 不会把 0、"" 或 nil 当作 false,所以 if count {} 和 if name {} 都无法编译。要显式比较:if count > 0、if name != ""、if user != nil。
用 &&(与)、||(或)和 !(非)组合条件。&& 和 || 都会短路求值,所以 nil 检查是安全的:
if user != nil && user.IsAdmin() {
// user.IsAdmin() is only called when user is not nil
}
带初始化语句的 if
if 可以在条件之前先执行一条简短语句,两者用分号隔开。在其中声明的变量在这个 if 的每个分支里都可见,在 if 之后则不可见:
这种写法让临时变量不会污染外层作用域。它在 map 查找(v, ok := m[k])、类型断言(s, ok := v.(string))和错误处理中随处可见。
反过来说:如果你需要在 if 之后使用这个值,就在 if 之前声明它。一个常见错误是把结果放进初始化语句,然后又想在下面使用它。
if err != nil 惯用法
可能失败的函数会把 error 放在最后一个返回值。立即检查它,处理或返回它,然后让正常流程不缩进地继续往下走:
注意,if err != nil { return ... } 之后没有 else。Go 的风格是处理错误、返回,并让正常流程保持在最左侧。错误处理页面会讲解如何包装错误以及检查错误类型。
用提前返回代替嵌套
层层嵌套的 if 块很难读。把条件反过来并提前返回,每个检查就成了一道守卫:
// Nested
func canCheckout(u *User, cart *Cart) bool {
if u != nil {
if u.Verified {
if len(cart.Items) > 0 {
return true
}
}
}
return false
}
// Guards
func canCheckout(u *User, cart *Cart) bool {
if u == nil || !u.Verified {
return false
}
if len(cart.Items) == 0 {
return false
}
return true
}
Go 程序员把这叫作让正常路径靠左对齐。revive 等 linter 会标记以 return 结尾的代码块之后的 else,因为这个 else 只是白白增加了缩进。
if/else 与 switch
一长串用同一个值去比较多种情况的 else if,改写成 switch 会更好读。不带表达式的 switch 可以代替由任意条件组成的 if/else if 链:
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
default:
return "F"
}
一两个分支用 if,三个或更多分支就用 switch。
没有三元运算符
Go 没有 cond ? a : b。用 if 来赋值:
label := "odd"
if n%2 == 0 {
label = "even"
}
三元运算符页面解释了原因,并介绍了替代写法。
常见错误
- 用 := 造成变量遮蔽。
if x, err := f(); err == nil { ... }声明了一个只属于这个if的新x,外层的x不会被更新。 - 在条件里赋值。
if x = 5 {}是编译错误(cannot use assignment x = 5 as value),而不是像 C 语言那样悄悄埋下 bug。请使用==。 - 用 == 比较浮点数。 对计算得到的值,
if total == 0.3会失败。要在一个容差范围内比较。 - 检查了错误的 error。 在
a, err := f()和b, err := g()之后,确保每个if err != nil都紧跟在对应的调用后面。
常见问题
Go 语言怎么写 if else?
条件两边不加圆括号,而且花括号必不可少:
if x > 10 {
fmt.Println("big")
} else if x > 5 {
fmt.Println("medium")
} else {
fmt.Println("small")
}
else 必须和上一个代码块的右花括号写在同一行。
Go 中带初始化语句的 if 是什么?
就是在条件前面加一条简短语句,用分号隔开:if n, err := strconv.Atoi(s); err != nil { ... }。在这里声明的变量只存在于这个 if 及其 else if 和 else 分支中,不会泄漏到函数的其余部分。
Go 里可以写单行 if 吗?
不加花括号不行。if ok return 和 if (ok) x = 1; 都是语法错误。最短的写法是 if ok { return },而 gofmt 会把语句体放到单独一行。Go 也没有三元运算符,所以要根据条件取值,就需要 if/else 或一个小的辅助函数。
为什么 Go 到处都是 if err != nil?
Go 没有异常。可能失败的函数会把一个 error 作为最后一个返回值,调用者立即用 if err != nil 检查它。这种重复换来的是:每个可能失败的地方都在出错的位置清清楚楚地写着。