Goの if は条件をかっこで囲まず、常に波かっこが必要で、if の中だけで使える変数を宣言する短い文から始めることもできます。
条件は上から順にチェックされ、最初に真になった分岐が実行されます。else if と else は省略できます。
構文のルール
C、Java、JavaScriptと異なるルールが3つあります。
- 条件を かっこで囲まない。
if (x > 5)もコンパイルできますが、gofmtがかっこを取り除きます。 - 文が1つでも 波かっこは必須。波かっこなしの
if x > 5 returnはありません。 elseは閉じ波かっこと同じ行に書く。Goは行末の}の後にセミコロンを挿入するので、次のコードは失敗します。
if x > 5 {
fmt.Println("big")
}
else {
fmt.Println("small")
}
syntax error: unexpected keyword else, expected }
} else { を1行に書きます。
条件は bool でなければなりません。Goは 0、""、nil を偽として扱わないので、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 では、条件の前にセミコロンで区切って短い文を1つ実行できます。そこで宣言した変数はその if のすべての分岐から見え、それ以降では見えません。
このパターンは、短命な変数を周りのスコープから締め出します。マップの参照(v, ok := m[k])、型アサーション(s, ok := v.(string))、エラー処理でいつも使われます。
その裏返しとして、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 などのリンターは、return で終わるブロックの後の else を指摘します。その else はインデントを増やすだけだからです。
if/elseとswitch
1つの値をいくつものケースと比べる長い else if の連鎖は、switch にしたほうが読みやすくなります。式のない switch は、任意の条件を並べた if/else if の連鎖の代わりになります。
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
default:
return "F"
}
分岐が1つか2つなら if を、3つ以上になったら 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 {}は、Cのように気づかないバグになるのではなく、コンパイルエラーになります(cannot use assignment x = 5 as value)。==を使います。 - 浮動小数点数を==で比較する。 計算した値に対する
if total == 0.3はうまくいきません。許容誤差を使って比較します。 - 違うエラーをチェックする。
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文を1行で書けますか?
波かっこなしでは書けません。if ok return や if (ok) x = 1; は構文エラーです。最も短い形は if ok { return } で、gofmt は本体を別の行に置きます。Goには三項演算子もないので、条件によって値を変えるには if/else か小さなヘルパー関数を使います。
なぜGoではif err != nilがあちこちに出てくるのですか?
Goには例外がありません。失敗しうる関数は最後の戻り値として error を返し、呼び出し側はすぐに if err != nil でそれをチェックします。この繰り返しは、失敗しうる場所がすべてその場で見えるようにするための代償です。