
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
算術演算子(Arithmetic operators)
算術演算子は数値に適用され、最初のオペランドと同じ型の結果を生成する。4つの標準算術演算子(+、-、*、/)は整数型、浮動小数点型、および複素数型に適用される。+ は文字列にも適用される。ビット論理演算子およびシフト演算子は整数にのみ適用される。
+ 和 整数, 浮動小数点数, 複素数値, 文字列
- 差 整数, 浮動小数点数, 複素数値
* 積 整数, 浮動小数点数, 複素数値
/ 商 整数, 浮動小数点数, 複素数値
% 剰余 整数
& ビットAND 整数
| ビットOR 整数
^ ビットXOR 整数
&^ ビットクリア(AND NOT) 整数
<< 左シフト 整数 << 0以上の整数
>> 右シフト 整数 >> 0以上の整数
オペランドの型が型パラメータである場合、演算子はその型集合の各型に適用可能でなければならない。オペランドは型パラメータがインスタンス化される型引数の値として表現され、演算はその型引数の精度で計算される。たとえば、以下の関数が与えられたとき:
func dotProduct[F ~float32|~float64](v1, v2 []F) F {
var s F
for i, x := range v1 {
y := v2[i]
s += x * y
}
return s
}
積 x * y と加算 s += x * y は、F の型引数に応じて、それぞれ float32 または float64 の精度で計算される。
解説
算術演算子の全体像
Go の算術演算子は大きく3グループに分かれます。
1. 基本の四則演算(+ – * /)
数値ならどれでも使えます。さらに + は文字列の連結にも使えます。
3 + 5 // 8(整数)
3.14 * 2.0 // 6.28(浮動小数点数)
(1+2i) + (3+4i) // (4+6i)(複素数)
"Hello" + " Go" // "Hello Go"(文字列連結)
2. 剰余(%)
整数専用です。割り算のあまりを求めます。
7 % 3 // 1
-7 % 3 // -1(Go では被除数の符号に従う)
3. ビット演算・シフト(& | ^ &^ << >>)
整数専用です。
0b1100 & 0b1010 // 0b1000(AND)
0b1100 | 0b1010 // 0b1110(OR)
0b1100 ^ 0b1010 // 0b0110(XOR)
0b1100 &^ 0b1010 // 0b0100(AND NOT = ビットクリア)
1 << 3 // 8(左シフト)
16 >> 2 // 4(右シフト)
結果の型は最初のオペランドと同じ
算術演算の結果は、最初のオペランドと同じ型になります。これは Go の「暗黙の型変換をしない」という方針と一致しています。
var a int32 = 10
var b int32 = 3
c := a / b // c は int32 型で値は 3(小数部分は切り捨て)
異なる型同士では演算できません。
var a int32 = 10
var b int64 = 3
c := a + b // コンパイルエラー! 型が違う
c := int64(a) + b // OK! 明示的に変換する
整数の除算は切り捨て
整数同士の / は、小数部分が切り捨てられます(ゼロに向かって切り捨て)。
7 / 3 // 2(3.5 ではない)
-7 / 3 // -2(-3 ではない。ゼロに向かう)
浮動小数点の結果が欲しい場合は、先に変換します。
float64(7) / float64(3) // 2.3333...
&^(ビットクリア)は Go 独自
&^ は他の言語にはあまり見られない Go 独自の演算子です。「右オペランドで指定したビットをクリア(0にする)」という意味です。
x := 0b1111 // 15
mask := 0b0110 // クリアしたいビット
result := x &^ mask // 0b1001 = 9
x & (^mask) と同じ結果になりますが、1つの演算子で書けるので便利です。権限フラグの管理などでよく使われます。
// 権限フラグの例
const (
Read = 1 << iota // 1
Write // 2
Execute // 4
)
perm := Read | Write | Execute // 7(全権限)
perm = perm &^ Write // 5(Write 権限を除去)
文字列の + 連結
+ は文字列の連結にも使えますが、大量の連結にはパフォーマンス上の注意が必要です。文字列は不変なので、+ のたびに新しい文字列が作られます。
// 少量の連結なら問題なし
greeting := "Hello" + " " + "World"
// ループ内で大量に連結するのは非効率
var s string
for i := 0; i < 10000; i++ {
s += "a" // 毎回新しい文字列が作られる → 遅い
}
// 代わりに strings.Builder を使う
var b strings.Builder
for i := 0; i < 10000; i++ {
b.WriteString("a") // 内部バッファに追記 → 速い
}
s := b.String()
型パラメータと算術演算の精度
ジェネリック関数で算術演算を行うとき、実際の計算精度はインスタンス化時の型引数によって決まります。
func dotProduct[F ~float32|~float64](v1, v2 []F) F {
var s F
for i, x := range v1 {
y := v2[i]
s += x * y // F の型に応じた精度で計算される
}
return s
}
// float32 で呼ぶと、計算精度は float32(約7桁)
v1 := []float32{1.0, 2.0, 3.0}
v2 := []float32{4.0, 5.0, 6.0}
dotProduct(v1, v2) // float32 精度
// float64 で呼ぶと、計算精度は float64(約15桁)
v3 := []float64{1.0, 2.0, 3.0}
v4 := []float64{4.0, 5.0, 6.0}
dotProduct(v3, v4) // float64 精度
同じソースコードでも、型引数によって計算結果が微妙に異なる場合があります。浮動小数点の丸め誤差は精度が低いほど大きくなるので、精度が重要な計算では float64 を使うのが一般的です。
おわりに
本日は、Go言語の言語仕様について解説しました。

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


コメント