
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
演算子の優先順位(Operator precedence)
単項演算子は最も高い優先順位を持つ。++ と -- 演算子は式ではなく文を構成するため、演算子の階層の外にある。その結果、文 *p++ は (*p)++ と同じになる。
二項演算子には5つの優先順位レベルがある。乗算演算子が最も強く結合し、続いて加算演算子、比較演算子、&&(論理AND)、最後に ||(論理OR)の順である:
優先順位 演算子
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
同じ優先順位の二項演算子は左から右へ結合する。たとえば、x / y * z は (x / y) * z と同じである。
+x // x
42 + a - b // (42 + a) - b
23 + 3*x[i] // 23 + (3 * x[i])
x <= f() // x <= f()
^a >> b // (^a) >> b
f() || g() // f() || g()
x == y+1 && <-chanInt > 0 // (x == (y+1)) && ((<-chanInt) > 0)
解説
優先順位表の読み方
数字が大きいほど優先順位が高い(先に計算される)ということです。算数の「掛け算は足し算より先」と同じ考え方ですね。
23 + 3*x[i]
// ステップ1: x[i] を評価(インデックスアクセスは演算子より先)
// ステップ2: 3 * x[i] (優先順位5)
// ステップ3: 23 + 結果 (優先順位4)
単項演算子は最強
単項演算子(+、-、!、^、*、&、<-)は、どの二項演算子よりも優先順位が高いです。
^a >> b // (^a) >> b であって、^(a >> b) ではない
-x + y // (-x) + y であって、-(x + y) ではない
^a はビット反転(NOT)で、まず a のビットを反転してから b だけ右シフトする、という意味になります。
++ と -- は「文」であって「式」ではない
C言語や JavaScript に慣れている方にとって、これは Go の大きな特徴です。
// C/JavaScript では式として使える
y = x++ // C では OK
// Go では文としてしか使えない
x++ // OK(文として単独で使う)
y = x++ // コンパイルエラー! x++ は値を返さない
だから *p++ は次のように解釈されます。
*p++
// ステップ1: *p でポインタをデリファレンス
// ステップ2: その値をインクリメント
// つまり (*p)++ と同じ
C言語では *(p++) と解釈される可能性がありますが、Go では ++ が式ではないのでそうなりません。この設計によって i++ + ++j のようなわかりにくいコードが原理的に書けなくなっています。
左から右への結合
同じ優先順位の演算子が並んだら、左から右の順で結合します。
x / y * z // (x / y) * z
42 + a - b // (42 + a) - b
これは算数の感覚と同じなので、自然に読めると思います。
複雑な式の読み解き方
原文の最後の例が一番複雑なので、丁寧に分解してみましょう。
x == y+1 && <-chanInt > 0
ステップ1:単項演算子を先に処理
<-chanInt → チャネルから受信(単項演算子、最優先)
ステップ2:優先順位5(乗算系)
該当なし。
ステップ3:優先順位4(加算系)
y+1 → 加算
ステップ4:優先順位3(比較系)
x == (y+1) と (<-chanInt) > 0 → 2つの比較
ステップ5:優先順位2(論理AND)
(x == (y+1)) && ((<-chanInt) > 0) → 全体の結合
結果:
(x == (y+1)) && ((<-chanInt) > 0)
実用的なアドバイス
優先順位を暗記する必要はありません。大事なのは2つだけです。
1. 迷ったら括弧をつける
// 読みにくい
result := a & b == 0
// 読みやすい
result := (a & b) == 0
括弧をつけることで、自分の意図を明示できますし、読む人も安心できます。
2. Go のビット演算子は C と優先順位が違う
C 言語では & や | は比較演算子より優先順位が低いですが、Go では乗算系(優先順位5)に分類されています。C から来た人は特に注意が必要です。
// Go では
a & b == 0 // (a & b) == 0 ← & が先(優先順位5 > 3)
// C では
a & b == 0 // a & (b == 0) ← == が先(C の & は == より低い)
Go のほうが直感的な順序になっていますが、C の癖がある人は混乱しやすいので、やはり括弧を書くのが安全です。
おわりに
本日は、Go言語の言語仕様について解説しました。

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


コメント