Goに while キーワードはありません。for の後に条件を書けば、それがwhileループです。
条件は毎回の反復の前にチェックされるので、最初から偽なら本体は一度も実行されません。C、Java、JavaScriptの while (balance < 2000) から、かっことキーワードを除いたものとまったく同じです。Goの設計者は、あらゆる形のループに1つのキーワードを選びました。4つの形はすべてforループのページで紹介しています。
while(true):無限ループ
条件をまるごと省くと、中の何かが終わらせるまでループが続きます。
Goらしい書き方は for { } です。for true { } もコンパイルできますが、書いた人が他の言語から来たように読めます。
無限ループを終わらせるものは4つあります。break(ループを抜ける)、return(関数を抜ける)、panic、そして os.Exit です。main がこれらのない for {} にとどまっているプログラムは、強制終了されるまで動き続けます。サーバーやワーカーにはまさにそれが正しい挙動です。
do-while
do-whileは、条件をチェックする前に本体を1回実行します。Goに専用の構文はありません。無限ループの最後にチェックを置きます。
本体は何があっても少なくとも1回実行されます。これがdo-whileを特徴づける性質です。乱数源は固定のシードを使っているので、実行するたびに同じ出力になります。本当にランダムにするには、シードを与えた乱数源を使わずに rand.IntN を使います。
2つ目の形として、最初の1回を強制してヘッダーに条件を残す方法もあります。
for first := true; first || condition(); first = false {
// body
}
これでも動きますが、最後に break を置いた無限ループのほうが読みやすく、ほとんどのGoのコードもそちらを使っています。
終わるまで読み取る
実際のGoのコードで最もよく見るwhileループは、入力がなくなるまで読み取るものです。bufio.Scanner はそのために設計されています。Scan は入力の終わりかエラーで false を返すので、そのままループの条件になります。
strings.NewReader(input) を os.Stdin に置き換えれば、同じループでユーザーの入力を1行ずつ読めます。ループの後で scanner.Err() をチェックしてください。Scan が false を返しただけでは、入力が正常に終わったのか失敗したのかわかりません。
breakとcontinue
どちらも for のすべての形で使えます。
条件だけのループでは、continue はそのまま条件に飛びます。そのため、気づきにくいバグを作りやすくなります。カウンタを本体の 最後 で更新していると、それより上の continue が更新を飛ばし、ループが終わらなくなります。上の例のようにカウンタを先頭で更新するか、continue の後でもpost文が実行される3つの節の for を使います。
何かを待つ
条件を待つループは、空回りするのではなく、ブロックするかスリープするべきです。
// Wrong: burns a CPU core while waiting
for !ready {
}
// Better: poll with a delay
for !isReady() {
time.Sleep(100 * time.Millisecond)
}
別のゴルーチンが書き込む普通の変数をポーリングするのは、データ競合でもあります。ゴルーチン間では、代わりにチャネル、sync.WaitGroup、sync.Cond で待つか、タイマー付きの select を使います。上限のあるリトライのループには、永遠に回ることがないように回数を数える for を使いましょう。
for attempt := 1; attempt <= 5; attempt++ {
if err := connect(); err == nil {
break
}
time.Sleep(time.Duration(attempt) * 200 * time.Millisecond)
}
よくある間違い
- 条件の変数を変え忘れる。
for i < 10 { fmt.Println(i) }は終わりません。どのwhileループにも、本体の中に出口へ近づける何かが必要です。 whileと書く。while x < 5 {}はsyntax error: unexpected name x at end of statementになります。whileはキーワードではないので、コンパイラは普通の名前として読みます。- 習慣でかっこを付ける。
for (x < 5) {}はコンパイルでき、gofmtがかっこを取り除きます。 - ループの中で変数を宣言する。
for n := 10; n > 0 {}は構文エラーです。初期化節のあるループには3つの節すべてが必要です。ループの前でnを宣言するか、3つの節の完全な形で書きます。
よくある質問
Goにwhileループはありますか?
キーワードとしてはありません。Goはすべてのループに for を使い、条件だけを書いた for がまさにwhileループです。
for count < 10 {
count++
}
CやJavaの while (count < 10) と同じく、条件は毎回の反復の前にチェックされます。
Goでwhile(true)はどう書きますか?
後ろに何も書かずに for とします:for { ... }。break、return、panic、os.Exit のいずれかで終わるまでループします。for true { ... } もコンパイルできますが、Goらしい書き方は for { } です。
Goでdo-whileループはどう書きますか?
無限ループを使い、条件のチェックを本体の最後に置きます。
for {
// body runs at least once
if !condition {
break
}
}
本体は条件がテストされる前に必ず1回実行されます。これがdo-whileの意味です。