Golang 入門

Go switch 文

Go 言語において、switchは複数の条件分岐を処理するためのクリーンかつ効率的な方法を提供します。長い if-else if-else ステートメントと比較して、switch 文は特に多くの条件判断が必要な場合に、コードの可読性と保守性を劇的に向上させます。

基本的なコントロールフロー構造として、変数や式の値に基づいて異なるコードブロックを実行することができます。

本章では、Go 言語における switch 文の構文、機能的な特徴、そして様々な実用的なユースケースについて深く掘り下げていきます。

1. switch 文の基本

最も基本的な switch 文は、式の値を一連の候補値と比較します。各候補値は case 節に関連付けられており、マッチングに成功すると、その case の下のコードブロックが実行されます。

1.1 基本構文

switch 文の一般的な構文は以下の通りです:

switch expression {
case value1:
    // expression == value1 の場合に実行されるコード
case value2:
    // expression == value2 の場合に実行されるコード
...
default:
    // 上記のどの case にもマッチしない場合に実行されるコード
}
  • expression (式): 評価される式です。変数、関数呼び出し、または任意の有効な Go の式を指定できます。
  • case value:case 節は一つの値を指定し、expression の結果と比較します。マッチした場合、その case 以降のコードが実行されます。
  • default: default ブランチはオプションです。存在する場合、どの case にもマッチしなかったときに実行されます(if-else における else と同様です)。

1.2 コード例

package main

import "fmt"

func main() {
    grade := "B"

    switch grade {
    case "A":
        fmt.Println("優秀!")
    case "B":
        fmt.Println("よくできました!")
    case "C":
        fmt.Println("合格。")
    case "D":
        fmt.Println("改善が必要です。")
    case "F":
        fmt.Println("不合格。")
    default:
        fmt.Println("無効な成績です。")
    }
}

この例では、switch 文が変数 grade の値を判断します。grade"B" であるため、プログラムは case "B": の下のコードを実行し、コンソールに「よくできました!」と出力します。もし grade がどのアルファベットにもマッチしない場合は、default ブランチが実行され、「無効な成績です。」と出力されます。

1.3 break 文の省略

C、C++、Java などの言語を学んだことがある方は、各 case の末尾にコードのフォールスルー(次の case への流出)を防ぐために break を書く習慣があるかもしれません。

Go 言語では break を書く必要はありません。 Go の switch 文は、最初にマッチした case ブロックを実行した後、自動的に switch 構造全体を抜けます。もし意図的に次の case へ処理を継続させたい場合は、明示的に fallthrough キーワードを使用する必要があります(詳細は後述します)。

2. 複数の値とのマッチング

Go 言語では、一つの case に複数のマッチング値をカンマ , 区切りで含めることができます。これにより、似たような状況を簡単に一つのグループにまとめることができます。

2.1 構文と実例

switch expression {
case value1, value2, value3:
    // expression が value1、value2、または value3 のいずれかであれば実行
...
}

平日と週末を判定する例を見てみましょう:

package main

import "fmt"

func main() {
    day := "Wed" // 水曜日

    switch day {
    case "Mon", "Tue", "Wed", "Thu", "Fri":
        fmt.Println("今日は平日です。")
    case "Sat", "Sun":
        fmt.Println("今日は週末です!")
    default:
        fmt.Println("無効な日付です。")
    }
}

ここでは、day の値が "Mon" から "Fri" のいずれかであれば、「今日は平日です。」と出力されます。

3. 式のない switch

Go の switch 文は、式を伴わずに記述することも可能です。この場合、各 case 節をブール値 (true または false) を返す条件判断として記述できます。プログラムは上から下へ計算し、最初に結果が true になった case を実行します。

これは実質的に、非常にエレガントに記述できる if-else if-else チェーンとなります。

3.1 構文と実例

switch {
case condition1:
    // condition1 が true の場合に実行
case condition2:
    // condition2 が true の場合に実行
...
default:
    // 上記の条件がすべて true でない場合に実行
}

年齢に基づいて層を分ける例を見てみましょう:

package main

import "fmt"

func main() {
    age := 25

    // 注意:switch の後に式がない
    switch {
    case age < 13:
        fmt.Println("子供")
    case age >= 13 && age < 20:
        fmt.Println("青少年")
    case age >= 20 && age < 65:
        fmt.Println("成人")
    default:
        fmt.Println("高齢者")
    }
}

age が 25 なので、最初に true と評価される条件は age >= 20 && age < 65 となり、「成人」と出力されます。

4. fallthrough 穿透(フォールスルー)文

fallthrough キーワードは、次の case の条件がマッチするかどうかにかかわらず、強制的に次の case のコードブロックを実行させるために使用されます。これは Go のデフォルトである「マッチしたら終了」というルールを打破します。

4.1 実例によるデモンストレーション

package main

import "fmt"

func main() {
    number := 2

    switch number {
    case 1:
        fmt.Println("一")
    case 2:
        fmt.Println("二")
        fallthrough // フォールスルーをトリガー!
    case 3:
        fmt.Println("三")
    default:
        fmt.Println("その他")
    }
}

number2 の時、上記のコードの出力結果は以下のようになります:

二
三

何が起きたのでしょうか? プログラムは case 2 にマッチし、「二」を出力しました。直後に fallthrough に遭遇したため、プログラムは強制的に隣接する case 3 のコードブロックに「蹴り込まれ」、number が実際には 3 でないことを完全に無視して「三」を出力しました。

注意:fallthrough は慎重に使用してください。乱用するとコードのロジックが極めて理解しにくくなる可能性があります。

5. 型スイッチ (Type switch)

型スイッチ (Type switch) は非常に特殊な switch 文で、変数の「値」ではなく、変数の「データ型」を判定します。

これは、実行時に様々な異なる低レイヤーの型の値を保持できる インターフェース (Interface) 型を処理する際に非常に有用です。

5.1 構文と実例

switch v := interfaceValue.(type) {
case type1:
    // この時、v の型は type1
case type2:
    // この時、v の型は type2
...
default:
    // どれにもマッチしない場合
}
  • interfaceValue: 実際の型を確認したいインターフェース変数。
  • v := interfaceValue.(type): これは型アサーションの特殊な構文です。型を判定するだけでなく、低レイヤーの実際の値を新しい変数 v に代入し、v は自動的にマッチした型になります。
package main

import "fmt"

// 空のインターフェースを受け取る。つまり、あらゆる型の値を渡せる
func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("これは整数です: %d\n", v)
    case string:
        fmt.Printf("これは文字列です: %s\n", v)
    case bool:
        fmt.Printf("これはブール値です: %t\n", v)
    default:
        fmt.Printf("未知の型です\n")
    }
}

func main() {
    describe(10)
    describe("Hello")
    describe(true)
}

この例では、describe 関数内の型スイッチが渡された値の低レイヤーの型を正確に識別し、対応する出力を生成しています。