
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
定数宣言(Constant declarations)
定数宣言は、識別子のリスト(定数の名前)を、定数式のリストの値に束縛します。識別子の数は式の数と等しくなければならず、左辺のn番目の識別子は右辺のn番目の式の値に束縛されます。
ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .
型が指定されている場合、すべての定数はその指定された型を取り、式はその型に代入可能でなければなりません。その型は型パラメータであってはなりません。型が省略された場合、定数はそれぞれ対応する式の個別の型を取ります。式の値が型なし定数(untyped constants)である場合、宣言された定数は型なしのままとなり、定数識別子はその定数値を表します。たとえば、式が浮動小数点リテラルである場合、たとえそのリテラルの小数部がゼロであっても、定数識別子は浮動小数点定数を表します。
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 型なし浮動小数点定数
const (
size int64 = 1024
eof = -1 // 型なし整数定数
)
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 型なし整数定数および文字列定数
const u, v float32 = 0, 3 // u = 0.0, v = 3.0
括弧で囲まれた const 宣言リストにおいて、最初の ConstSpec を除く任意の ConstSpec から式リストを省略することができます。このような空のリストは、直前の空でない式リストとその型(もしあれば)をテキスト上で置換したものと同等です。したがって、式リストの省略は、直前のリストを繰り返すことと同等です。識別子の数は、直前のリストの式の数と等しくなければなりません。iota 定数ジェネレータと組み合わせることで、この仕組みにより連続した値の軽量な宣言が可能になります。
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Partyday
numberOfDays // この定数はエクスポートされない
)
解説
たとえ話
定数宣言は、石に文字を刻む行為に例えられます。
変数が「ホワイトボードに書いたメモ」(いつでも書き換えられる)だとすると、定数は「石碑に刻んだ文字」です。一度刻んだら二度と変えることはできません。
そして iota を使った連番生成は、自動番号スタンプ機のようなものです。最初に0番からスタートし、行が変わるたびに自動的に番号が1つずつ増えていきます。同じパターンを繰り返す場合は式を省略でき、スタンプ機が前の行と同じ処理を自動的に繰り返してくれます。
コード例
package main
import "fmt"
// ══════════════════════════════════════
// 基本的な定数宣言
// ══════════════════════════════════════
// 型を明示した定数:必ずその型になる
const Pi float64 = 3.14159265358979323846
// 型を省略した定数:「型なし定数」になる
const zero = 0.0 // 型なし浮動小数点定数(int にも float64 にも柔軟に使える)
// 複数の定数を一度に宣言
const a, b, c = 3, 4, "foo" // 左辺と右辺の数が一致していること
func main() {
fmt.Println(Pi) // → 3.141592653589793
fmt.Println(zero) // → 0
fmt.Println(a, b, c) // → 3 4 foo
}
package main
import "fmt"
// ══════════════════════════════════════
// 型あり定数 vs 型なし定数の違い
// ══════════════════════════════════════
const typedZero float64 = 0.0 // 型あり:float64 に固定
const untypedZero = 0.0 // 型なし:文脈に応じて柔軟に型が決まる
func main() {
var f float64 = untypedZero // ✅ float64 として使える
var i int = int(untypedZero) // 型なしでも明示的に変換は必要な場合がある
// var j int = typedZero // ❌ コンパイルエラー:float64 を int に直接代入できない
fmt.Println(f, i)
}
package main
import "fmt"
// ══════════════════════════════════════
// 括弧付き const と式の省略ルール
// ══════════════════════════════════════
const (
first = 100 // first = 100
second // 式を省略 → 直前と同じ式が使われる → second = 100
third // 同様に → third = 100
)
func main() {
fmt.Println(first, second, third) // → 100 100 100
}
package main
import "fmt"
// ══════════════════════════════════════
// iota を使った連番定数の生成
// ══════════════════════════════════════
// iota は括弧付き const ブロック内で 0 から始まり、
// 行(ConstSpec)ごとに 1 ずつ増える特別な定数ジェネレータ
const (
Sunday = iota // 0
Monday // 1(式を省略 → iota が繰り返される)
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println("日曜日:", Sunday) // → 0
fmt.Println("水曜日:", Wednesday) // → 3
fmt.Println("土曜日:", Saturday) // → 6
}
package main
import "fmt"
// ══════════════════════════════════════
// iota の応用パターン
// ══════════════════════════════════════
// ビットフラグ(1, 2, 4, 8, ...)
const (
ReadPermission = 1 << iota // 1 << 0 = 1
WritePermission // 1 << 1 = 2
ExecutePermission // 1 << 2 = 4
)
// 単位変換
const (
_ = iota // 0 を捨てる(ブランク識別子)
KB = 1024 * iota // 1024 * 1 = 1024
MB // 1024 * 2 = 2048 ← ⚠ これは意図と違うかも!
)
// 正しい単位変換はこう書く
const (
_ = iota
KiB = 1 << (10 * iota) // 1 << 10 = 1024
MiB // 1 << 20 = 1048576
GiB // 1 << 30 = 1073741824
)
func main() {
fmt.Println("読み取り:", ReadPermission) // → 1
fmt.Println("書き込み:", WritePermission) // → 2
fmt.Println("実行:", ExecutePermission) // → 4
// ビットフラグの組み合わせ
myPerm := ReadPermission | WritePermission
fmt.Println("読み書き:", myPerm) // → 3
fmt.Println("1 KiB =", KiB, "bytes") // → 1024
fmt.Println("1 MiB =", MiB, "bytes") // → 1048576
fmt.Println("1 GiB =", GiB, "bytes") // → 1073741824
}
よくある間違い・注意点
1. 定数には代入し直せない
const x = 10
// x = 20 // ❌ コンパイルエラー:定数は再代入できない
2. iota は const ブロックごとにリセットされる
const (
a = iota // 0
b // 1
)
const (
c = iota // 0 ← 新しいブロックなのでリセット!1 ではない
d // 1
)
3. 式の省略は「直前の式をそのまま繰り返す」であって「直前の値をコピーする」ではない
const (
x = iota * 2 // 0 * 2 = 0
y // 1 * 2 = 2(「0 を繰り返す」のではなく「iota * 2 を繰り返す」)
z // 2 * 2 = 4
)
式そのものが繰り返されるため、iota の値が変わると結果も変わります。これは式の「テキスト上の置換」であることを理解しておくと間違えにくくなります。
4. 型なし定数の柔軟さを活かす
型を明示しなくてよい場面ではあえて省略することで、型なし定数の柔軟さを享受できます。型なし定数は使われる文脈に応じて適切な型に自動変換されるため、不必要な型変換を避けられます。
const ratio = 0.5 // 型なし → int の計算にも float64 の計算にも使える
func main() {
var f float64 = ratio * 100 // ✅ float64 として使われる
var f32 float32 = ratio // ✅ float32 にもなれる
}
まとめ
- 定数宣言は、
constキーワードで識別子を不変の値に束縛する仕組み。左辺と右辺の数は一致させる必要がある。 - 型あり定数はその型に固定されるが、型なし定数は使用される文脈に応じて柔軟に型が決まる。
- 括弧付き
constブロックでは、式を省略すると直前の式がテキスト上で繰り返される。 iotaはconstブロック内で行ごとに 0 から自動的にインクリメントされる特殊な定数ジェネレータ。ブロックが変われば 0 にリセットされる。iotaとビットシフトの組み合わせで、フラグやサイズ単位などの実用的な定数群を簡潔に定義できる。
iota の「式を省略すると前の式を繰り返す」という仕組みは、一見シンプルですが非常に強力です。この仕組みを理解しておくと、Goらしいクリーンな定数定義が書けるようになりますよ!
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント