Golang の switch 条件分岐(switch の基本、便利な使い方、型スイッチ)

Go 言語の switch 文は、Java や C++ に比べて簡潔に記述できるようになっています。

switch 文の基本

Go 言語の switch 文では、case に複数の値をカンマで区切って指定することができます。 1つの case が実行されると自動的に switch 文を終了するため、C 言語のように case ごとに break と記述する必要はありません。 逆に、次の case を続けて実行したい場合は、明示的に fallthrough と記述する必要があります。

func checkNumber(i int) {
	switch i {
	case 0:
		fmt.Println("zero")
	case 2, 3, 5, 7:
		fmt.Println("primary number")
		fallthrough
	default:
		fmt.Println("good number")
	}
}

if 文の代わりに switch 文を使うことでコードを簡潔にできることがあります。

func shouldEscape(c byte) bool {
	switch c {
	case ' ', '?', '&', '=', '#', '+', '%':
		return true
	}
	return false
}

if 文と同様に、switch 文でも変数のスコープをブロック内に絞った変数定義を行うことができます。 次の os 変数は、switch 文の中でのみ参照できます。

switch os := runtime.GOOS; os {
case "darwin":
	fmt.Println("OS = OS X")
case "linux":
	fmt.Println("OS = Linux")
default:
	// freebsd, openbsd, plan9, windows...
	fmt.Printf("OS = %s\n", os)
}

連続する if else の代わりとして switch 文を使用する

switch 文の条件部分を省略 すると、switch true と記述するのと同様の振る舞いをします。 この記述方法は、連続した if else を簡潔に記述するために使用することができます。 典型的なのは、ある変数の値を大小比較したいケースです。

func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0'
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10
	}
	return 0
}

もう 1 つ例を。

func greet() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning.")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

上記の変数 t のスコープを switch 文の内部に絞りたいのであれば、下記のように変数定義します(セミコロンの後ろの条件部分だけを省略します)。

switch t := time.Now(); {
case t.Hour() < 12:
	fmt.Println("Good morning.")
case t.Hour() < 17:
	fmt.Println("Good afternoon.")
default:
	fmt.Println("Good evening.")
}

ちなみに上記の switchif else を使って書き換えると次のようになります。

if t := time.Now(); t.Hour() < 12 {
	fmt.Println("Good morning!")
} else if t.Hour() < 17 {
	fmt.Println("Good afternoon.")
} else {
	fmt.Println("Good evening.")
}

まぁ、このくらいであればそれほど差はありませんが、それでも switch の方が可読性は高そうです。

型スイッチ (Type Switch)

Go 言語の関数で任意の型の引数を受け取るには、空インタフェース型 interface{} を使用します。 オブジェクトの実際の型に基づいて分岐処理を行うには、次のように 型スイッチ (Type Switch) の仕組みを使います。

func checkType(value interface{}) {
	switch v := value.(type) {
	case nil:
		fmt.Println("value is nil")
	case int:
		fmt.Printf("value is int (%d)\n", v)
	case float64:
		fmt.Printf("value is float64 (%f)\n", v)
	case func(int) string:
		fmt.Println("value is function that takes int and returns string")
	case bool, string:
		fmt.Println("value is bool or string")
	default:
		fmt.Printf("value has unknown type (%T)\n", v)
	}
}

上記の例では、value 変数の実際の型によって分岐処理を行っています。 さらに、分岐後は v 変数をその型の値として参照することができます。

型スイッチは、型アサーション (Type Assertion) の特殊形態だと考えることができます。