
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
式switch(Expression switches)
式switch では、switch式が評価され、定数である必要のないケース式が左から右、上から下の順に評価される。switch式と最初に等しくなったケース式が、関連するケースの文の実行を引き起こす。他のケースはスキップされる。どのケースも一致せず default ケースがある場合、その文が実行される。default ケースは最大1つであり、switch 文のどこにでも配置できる。switch式が省略された場合、真偽値 true と等価である。
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
switch式が型なし定数と評価される場合、まず暗黙的にそのデフォルト型に変換される。事前宣言された型なし値 nil はswitch式として使用できない。switch式の型は比較可能でなければならない。
ケース式が型なしの場合、まず暗黙的にswitch式の型に変換される。各(変換された可能性のある)ケース式 x とswitch式の値 t について、x == t は有効な比較でなければならない。
言い換えれば、switch式は明示的な型なしで一時変数 t を宣言し初期化するかのように扱われる。各ケース式 x はその t の値に対して等値テストされる。
case 節または default 節において、最後の空でない文は、制御がこの節の終わりから次の節の最初の文へ流れるべきことを示す(ラベル付きの場合もある)fallthrough 文であってよい。そうでなければ、制御は switch 文の終わりへ流れる。fallthrough 文は式switch の最後の節を除くすべての節の最後の文として現れることができる。
switch式の前に単純文を置くことができ、それは式が評価される前に実行される。
switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch x := f(); { // switch式の省略は "true" を意味する
case x < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
実装上の制限:コンパイラは同じ定数と評価される複数のケース式を禁止することがある。たとえば、現在のコンパイラはケース式における重複する整数、浮動小数点、または文字列定数を禁止する。
解説
式switchの基本
式switchは、switch式の値と各ケースの値を == で比較し、最初に一致したケースを実行します。
switch day {
case "Monday":
fmt.Println("月曜日")
case "Tuesday":
fmt.Println("火曜日")
case "Wednesday":
fmt.Println("水曜日")
default:
fmt.Println("その他の曜日")
}
C言語との最大の違い:自動 break
Go の switch では、一致したケースの処理が終わると自動的に switch 文を抜けます。C 言語のように break を書く必要がありません。
// Go:break 不要。case 1 の処理が終わると switch を抜ける
switch x {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
}
// C 言語:break を忘れると次のケースに落ちる(フォールスルー)
// switch (x) {
// case 1: printf("one"); // break がないと...
// case 2: printf("two"); // ここも実行される!
// }
1つの case に複数の値
カンマで区切って複数の値を1つの case に書けます。
switch ch {
case 'a', 'e', 'i', 'o', 'u':
fmt.Println("母音")
case ' ', '\t', '\n':
fmt.Println("空白文字")
default:
fmt.Println("その他")
}
C 言語では同じことを書くのに各値ごとに case を並べる必要がありますが、Go ではすっきり書けます。
条件なし switch:if-else の代替
switch式を省略すると、true と比較されます。つまり、各 case に任意の条件式を書けます。
switch {
case x < 0:
fmt.Println("負の数")
case x == 0:
fmt.Println("ゼロ")
case x > 0:
fmt.Println("正の数")
}
これは if-else if-else とまったく同じ意味ですが、条件が多い場合は switch のほうが見やすくなります。
// if-else if で書くと
if score >= 90 {
grade = "A"
} else if score >= 80 {
grade = "B"
} else if score >= 70 {
grade = "C"
} else {
grade = "F"
}
// 条件なし switch で書くと
switch {
case score >= 90:
grade = "A"
case score >= 80:
grade = "B"
case score >= 70:
grade = "C"
default:
grade = "F"
}
初期化文つき switch
if 文と同じように、switch式の前に初期化文を置けます。
switch x := f(); {
case x < 0:
return -x
default:
return x
}
x のスコープは switch 文全体に限定されます。
default の位置は自由
default ケースはどこにでも置けます。原文の例では先頭に置かれています。
switch tag {
default: s3() // 一番上でも OK
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
ただし、慣例として default は最後に書くことが多いです。読み手にとって自然な順序だからです。
fallthrough:明示的なフォールスルー
Go では意図的に次のケースに落としたい場合、fallthrough を明示的に書きます。
switch x {
case 1:
fmt.Println("1")
fallthrough // 次の case に落ちる
case 2:
fmt.Println("2") // x==1 のときもここが実行される
case 3:
fmt.Println("3") // ここには落ちない
}
// x==1 の場合の出力:
// 1
// 2
fallthrough にはいくつかの制限があります。
switchの最後のケースには書けない(落ちる先がないため)fallthroughの後に他の文を書けない(ケースの最後の文でなければならない)- 次のケースの条件はチェックされない(無条件に次のケースの本体に入る)
実際のところ、fallthrough はめったに使いません。ほとんどの場合、複数の値を1つの case にまとめるか、別のロジックに書き換えるほうがわかりやすいです。
ケース式の評価順序
ケース式は上から下、左から右の順に評価されます。最初に一致したケースが実行されたら、残りは評価すらされません。
switch x {
case expensive1(): // まずこれを評価
// ...
case expensive2(): // 上が一致しなければこれを評価
// ...
case expensive3(): // さらに一致しなければこれを評価
// ...
}
これは短絡評価と同じ考え方で、不要な計算を避けてくれます。
重複する定数は禁止
同じ定数値を複数の case に書くと、現在のコンパイラではエラーになります。
switch x {
case 1:
// ...
case 1: // コンパイルエラー! 重複する定数
// ...
}
ただしこれは仕様上の要件ではなく「実装上の制限」なので、変数やランタイムに評価される式は重複しても許されます。
おわりに
本日は、Go言語の言語仕様について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント