Go言語を用いた開発において、繰り返し処理の制御はプログラムの論理構造を決定づける重要な要素です。

効率的なコードを書くためには、ループ内での条件分岐を適切に管理し、不要な処理をスキップする技術が欠かせません。

本記事では、Go言語の制御構文の一つである continue 文に焦点を当て、その基本的な使い方から応用的なラベル制御までを詳しく解説します。

初心者の方から、より複雑な多重ループの制御に悩む中級者の方まで、実践で役立つ知識を深めていきましょう。

continue文の基本概念と役割

Go言語の continue 文は、現在実行中の for ループの反復処理をその時点で中断するために使用されます。

処理を中断した後はループ自体を抜けるのではなく、ループの次のステップへと直ちに移行します。

これにより、特定の条件に合致した場合のみ特定の処理をスキップするという制御が容易になります。

Go言語では繰り返し構文として for しか存在しないため、 continue の使い所を理解することは、ループ制御のすべてを理解することに繋がります。

continue文の基本的な構文

continue 文の記述は非常にシンプルで、キーワードのみを記述します。

通常は if 文などの条件分岐と組み合わせて、特定の条件が満たされた際に実行されるように配置します。

Go
for i := 0; i < 10; i++ {
    if i%2 == 0 {
        // iが偶数の場合、これ以降の処理をスキップして次のループへ進む
        continue
    }
    fmt.Println(i)
}

上記の例では、 i が偶数のときには fmt.Println(i) が実行されず、インクリメント式 i++ へ制御が戻ります。

この挙動により、奇数のみを出力するプログラムを簡潔に記述できるのです。

基本的な活用例:特定の数値をスキップする

まずは、最も一般的である「特定の条件に該当するデータを除外して処理を継続する」例を見てみましょう。

例えば、1から10までの数値の中で、3の倍数だけを除外して出力したい場合に continue は威力を発揮します。

具体的なソースコード

以下に、3の倍数をスキップするプログラムの実装例を示します。

Go
package main

import "fmt"

func main() {
    fmt.Println("3の倍数以外を表示します:")

    for i := 1; i <= 10; i++ {
        // 3で割り切れるかチェック
        if i%3 == 0 {
            // 条件に合致した場合は、以降の処理を飛ばす
            continue
        }
        // スキップされなかった数値のみが表示される
        fmt.Printf("%d ", i)
    }
    fmt.Println("\n処理が完了しました。")
}

上記のコードを実行すると、以下のような結果が得られます。

実行結果
3の倍数以外を表示します:
1 2 4 5 7 8 10 
処理が完了しました。

結果から分かる通り、3、6、9という数値がスキップされ、それ以外の数値が順に出力されています。

このように、 continue 文を使うことで、条件に合わないデータを「早期に無視する」ロジックを構築できます。

ラベル付きcontinueによる多重ループの制御

Go言語のユニークな機能の一つに、ラベルを利用した制御構文があります。

通常の continue 文は、最も内側にある for ループに対してのみ作用します。

しかし、二重、三重に重なったネスト構造のループにおいて、外側のループの次の反復へスキップしたい場合も存在します。

このようなケースで活躍するのが、ラベルを指定した continue 文です。

ラベル付きcontinueの構文

ラベルは、 for ループの直前に ラベル名: という形式で記述します。

そして continue ラベル名 と記述することで、そのラベルが付与されたループの更新式へジャンプします。

ラベルを使用した実装例

以下の例では、親ループと子ループがある中で、特定の条件時に親ループの次のステップへ戻る処理を記述しています。

Go
package main

import "fmt"

func main() {
OuterLoop: // ラベルを定義
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i == 2 && j == 2 {
                fmt.Printf("スキップ: i=%d, j=%d\n", i, j)
                // OuterLoop(外側のループ)の次のステップへ飛ぶ
                continue OuterLoop
            }
            fmt.Printf("処理中: i=%d, j=%d\n", i, j)
        }
    }
}

このプログラムを実行すると、動作の違いが明確になります。

実行結果
処理中: i=1, j=1
処理中: i=1, j=2
処理中: i=1, j=3
処理中: i=2, j=1
スキップ: i=2, j=2
処理中: i=3, j=1
処理中: i=3, j=2
処理中: i=3, j=3

i=2, j=2 のタイミングで continue OuterLoop が実行されました。

その結果、 j=3 の処理が行われることなく、外側のループである i=3 の処理が開始されています。

ラベル付き continue を使わなければ、フラグ変数などを用意して制御する必要があるため、非常に便利な機能です。

continue文とbreak文の違い

ループ制御において continue と混同しやすいのが break 文です。

これら二つのキーワードは、どちらもループの通常の流れを変えるものですが、その目的は大きく異なります。

正しいプログラムを書くためには、この違いを明確に把握しておく必要があります。

動作の比較表

continue 文と break 文の違いを下表にまとめました。

項目continue文break文
主な目的現在の回をスキップして次へ進むループ自体を直ちに終了する
実行後の動きfor の更新式(i++など)へ移動ループの波括弧 } の直後へ移動
利用シーンフィルタリング、特定値の無視検索完了時の脱出、エラー時の中断

簡潔に言えば、 「やり直し」が continue で、「切り上げ」が break です。

処理を途中で投げ出さずに最後までリストを走査したい場合は、必ず continue を選択してください。

実践的な活用シーン:ガード節としてのcontinue

プログラミングにおいてコードの可読性を高めるテクニックの一つに「ガード節」があります。

continue 文をガード節として利用することで、関心の対象外となる条件をループの先頭で排除できます。

これにより、本来行いたいメインの処理が if 文のネストの中に埋もれてしまうのを防ぐことが可能です。

ネストを深くしないための工夫

以下の二つのコードを比較してみましょう。

まずは、 continue を使わずに if 文で処理を囲むパターンです。

Go
for _, user := range users {
    if user.IsActive {
        if user.HasPermission {
            // ここにメインの長い処理が書かれる
            process(user)
        }
    }
}

次に、 continue をガード節として使用したパターンです。

Go
for _, user := range users {
    if !user.IsActive {
        continue
    }
    if !user.HasPermission {
        continue
    }
    
    // メインの処理をネストなしで記述できる
    process(user)
}

後者の書き方は「条件に合わない場合はさっさと次へ行く」という意図が明確です。

コードが左側に揃うため、ロジックの流れが追いやすくなり、保守性が向上します。

実務的な開発現場では、このような「ネストの浅いコード」を書くことが強く推奨されます。

continue文を使用する際の注意点

continue 文は非常に便利ですが、使用にあたってはいくつかの注意点があります。

特に無限ループや、期待しないカウンタ変数の制御に注意を払う必要があります。

無限ループのリスク

for 文の形式によっては、 continue が原因で無限ループに陥る可能性があります。

通常の for i := 0; i < 10; i++ という形式であれば、 continue を実行しても i++ が呼ばれるため安全です。

しかし、 while ループのように条件式のみを記述し、ループ内で手動でカウンタを更新している場合は注意が必要です。

Go
i := 0
for i < 10 {
    if i == 5 {
        // i++ を呼ぶ前に continue すると、i は 5 のまま固定される
        continue 
    }
    fmt.Println(i)
    i++
}

上記のコードは、 i が5になった瞬間に continue され、それ以降 i++ が実行されることはありません。

その結果、条件 i < 10 は永遠に真となり続け、プログラムはハングアップしてしまいます。

カウンタの更新タイミングと continue の位置関係には、常に細心の注意を払いましょう。

過度な使用による可読性の低下

continue を多用しすぎると、ループ内のどこで制御が飛んでいるのかを把握するのが難しくなる場合があります。

一つのループ内に複数の continue が散らばっている場合は、関数への切り出しやロジックの整理を検討すべきサインかもしれません。

まとめ

Go言語における continue 文は、ループ処理をスマートかつ柔軟に制御するための強力なツールです。

基本的なスキップ処理から、ラベルを用いた高度な多重ループ制御まで、その用途は多岐にわたります。

特に、ガード節として continue を活用することで、コードのネストを浅く保ち、読みやすいプログラムを作成できる点は大きなメリットです。

一方で、手動でカウンタを管理するループにおいては、無限ループを引き起こすリスクがあることも忘れてはいけません。

今回学んだ continue 文の特性を活かし、よりクリーンで効率的なGoプログラムの実装に役立ててください。

適切な制御構文の選択こそが、優れたエンジニアへの第一歩となります。