Menu

Go言語のfor文:4つの書き方をすべて例で解説

Goのループのキーワードはforだけで、これですべてをまかないます。3つの節からなるカウンタのループ、条件だけのwhileループ、無限ループ、そしてスライス、マップ、文字列、チャネル、整数、関数に対するrange。ラベル付きのbreakとcontinue、Go 1.22でのループ変数の変更も扱います。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

Goのループのキーワードは for の1つだけです。形は4つあり、それらで他の言語の forwhiledo whileforeach で書くループをすべてまかないます。

ループのヘッダーをかっこで囲むことはなく、波かっこは常に必要です。

3つの節からなるループ

for init; condition; post {
	// body
}
  1. init はループの前に一度だけ実行されます。たいていは i := 0 のような短い変数宣言で、その変数はループの中にだけ存在します。
  2. condition は毎回の反復の前にチェックされます。偽になるとループが終わります。
  3. 本体が実行されます。
  4. post は各反復の後に実行されます。典型的には i++ です。

Goでは i++ は式ではなく文なので、posti++, j-- とは書けません。代わりに多重代入を使います:i, j = i+1, j-1

3つの節はどれも省略できます。for ; i < 10; {} も正しく、gofmt はこれを for i < 10 {} に書き換えます。これが次の形です。

条件だけのループ(Goのwhile)

条件だけを書くと、for は他の言語の while のように振る舞います。

別に do while はありません。「少なくとも1回は実行する」挙動の作り方はwhileループのページで紹介しています。

無限ループ

後ろに何も書かない for は永遠にループします。breakreturnos.Exit、またはpanicで抜けます。

for {
	line, err := reader.ReadString('\n')
	if err != nil {
		break
	}
	process(line)
}

これはサーバー、ワーカー、入力ループの標準的な形です。本体が空の for {} はCPUコアを1つ使い切ります。実際のコードでは、本体が何か(読み取り、チャネルからの受信、select)でブロックします。

rangeループ

range はコレクションを走査し、反復ごとに1つまたは2つの値を返します。それが何になるかは、何に対してrangeするかで決まります。

rangeの対象1つ目の値2つ目の値
スライスまたは配列インデックス要素(コピー)
文字列バイトインデックスルーン(Unicodeの文字)
マップキー
チャネル要素(なし)
整数 n(Go 1.22)0からn-1(なし)
イテレータ関数(Go 1.23)関数が渡すもの関数が渡すもの

出力からわかる細かい点が3つあります。

  • 値の変数は コピー です。ループの中で v *= 2 としてもスライスは変わりません。要素を変更するには、3つ目のループのように nums[i] でスライスにインデックスを使います。
  • 文字列のインデックスは バイト オフセットです。é は2バイトを使うので、インデックスは1から3に飛びます。
  • マップの順序は意図的に ランダム です。コードが順序に依存できないように、Goがランダムにしています。slices.Sorted(maps.Keys(m))(Go 1.23)でキーを順に取り出せます。

チャネル、イテレータ関数、コピーのルールを含め、range の全体はrangeのページで扱っています。

breakとcontinue

break は最も内側のループを終わらせます。continue は次の反復に飛びます(3つの節のループでは先に post を実行します)。

ラベル付きのbreakとcontinue

普通の break は最も内側のループしか抜けません。ネストしたループの中から外側のループを抜けるには、外側のループにラベルを付けます。

ラベルは識別子の後にコロンを付けたもので、for の直前に置きます。使われていないラベルは、未使用の変数と同じくコンパイルエラーとして報告されます。

switchとselectの中のbreak

for の中で、switchselect のケースにある break が抜けるのはループではなく switchselect です。

for {
	switch cmd := next(); cmd {
	case "quit":
		break // only exits the switch; the loop keeps running
	}
}

switchselect の中からループを抜けるには、ラベル付きの breakreturn を使います。

Go 1.22以降のループ変数

Go 1.22でループ変数のスコープが変わり、反復ごとに iv新しいコピー が作られるようになりました。以前はループ全体で1つの変数が使い回されていたので、それを捕捉したクロージャやゴルーチンが壊れていました。

Go 1.22以降では、これは 0 1 2[aa bb cc] を表示します。古いセマンティクスでは、同じプログラムを for i := 0; i < 3; i++ で書くと(整数に対するrangeは1.22より前には存在しませんでした)3 3 3 と表示され、ゴルーチンは共有の iname で競合していました。ループ本体の先頭に i := i と書く昔の回避策は、もう必要ありません。

この挙動はコンパイラのバージョンではなく、go.modgo 行によってモジュールごとに決まります。go 1.21 のままのモジュールは、Go 1.24でビルドしても古いセマンティクスのままです。

よくある間違い

コピーを変更している。 for _, v := range items { v.Count++ } はコピーを変更しています。items[i].Count++ を使うか、ポインタのスライスに対してrangeします。

rangeしながらappendする。 range はスライスを最初に一度だけ評価します。ループ内で追加した要素は走査されないので、ループはちゃんと終わりますが、新しい要素は見えません。

前向きのループでインデックスを使ってスライスから削除する。 要素 i を削除すると残りが左にずれるので、次の要素が飛ばされます。新しいスライスを作るか、後ろからループするか、slices.DeleteFunc を使います。

<=による1つずれ。 for i := 0; i <= len(s); i++ は最後の反復で s[len(s)] を読み、index out of range でpanicします。< を使うか、もっと良いのは range を使うことです。

条件の中の重い処理。 条件は毎回の反復で実行されます。for i := 0; i < expensiveCount(); i++ は毎回その関数を呼ぶので、ループの前に一度だけ計算します。

浮動小数点数をカウンタに使う。 0.1 は2進数で正確に表せないので、for x := 0.0; x != 1.0; x += 0.1 は終わりません。整数で数え、そこから浮動小数点数を計算します。

よくある質問

Goでforループはどう書きますか?

古典的な形は、セミコロンで区切った3つの部分からなり、かっこは付けません。

for i := 0; i < 5; i++ {
	fmt.Println(i)
}

Go 1.22以降は for i := range 5 とも書け、0から4まで数えます。スライスをたどるには for i, v := range items を使います。

Goにwhileループはありますか?

while キーワードはありませんが、条件だけを書いた for がwhileループです:for n > 0 { n /= 2 }。条件のない for { ... } は無限ループで、breakreturn で抜けます。

Goでマップをループするには?

for key, value := range m { ... } です。反復の順序は規定されておらず、ランタイムがランダムにするので、ループのたびに変わることがあります。順序に頼ってはいけません。安定した順序がほしいなら、キーを集めて slices.Sort でソートし、ソートしたキーでループします。

Goでネストしたループから抜けるには?

外側のループの前にラベルを置き、break でその名前を指定します:outer: for ... { for ... { if found { break outer } } }。普通の break は最も内側の forswitchselect だけを抜けます。continue outer も同様に、ラベルを付けたループの次の反復に飛びます。

Go 1.22でループ変数について何が変わりましたか?

Go 1.22より前は、for ループの変数はすべての反復で共有される1つだけだったので、ループ内で作ったクロージャやゴルーチンがみな最後の値を見てしまうことがよくありました。Go 1.22以降は反復ごとに独自のコピーが作られるので、クロージャで iv を捕捉しても期待どおりに動きます。新しい挙動は、モジュールの go.modgo 1.22 以降を指定している場合に適用されます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める