
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
型宣言(Type declarations)
型宣言は、識別子(型名)を型に束縛します。型宣言には、エイリアス宣言(alias declarations)と型定義(type definitions)の2つの形式があります。
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
解説
たとえ話
型宣言を名前の付け方に例えると、2つの形式の違いがよく分かります。
エイリアス宣言は、あだ名(ニックネーム)をつけるようなものです。「田中太郎」さんに「たっちゃん」というあだ名をつけても、本人はまったく同じ人物です。元の型と完全に同一であり、区別されません。
型定義は、独立した新しい人格を生み出すようなものです。親(元の型)と同じ見た目・能力を持っていますが、戸籍上は別人です。別の型として扱われるため、明示的な変換なしには混ぜて使えません。
コード例
package main
import "fmt"
// ══════════════════════════════════════
// エイリアス宣言(= を使う)
// ══════════════════════════════════════
// MyInt は int のあだ名。完全に同じ型として扱われる
type MyInt = int
// ══════════════════════════════════════
// 型定義(= を使わない)
// ══════════════════════════════════════
// UserID は int に基づく新しい型。int とは別物として扱われる
type UserID int
func main() {
// --- エイリアスの場合 ---
var a MyInt = 10
var b int = a // ✅ MyInt と int はまったく同じ型なので直接代入できる
fmt.Println(b)
// --- 型定義の場合 ---
var id UserID = 42
// var c int = id // ❌ コンパイルエラー!UserID と int は別の型
var c int = int(id) // ✅ 明示的に変換すればOK
fmt.Println(c)
}
package main
import "fmt"
// ══════════════════════════════════════
// 括弧を使って複数の型をまとめて宣言
// ══════════════════════════════════════
type (
// 型定義
Celsius float64
Fahrenheit float64
// エイリアス宣言
Text = string
)
func main() {
var tempC Celsius = 100.0
var tempF Fahrenheit = 212.0
// Celsius と Fahrenheit は別の型(両方 float64 ベースだが区別される)
// tempC = tempF // ❌ コンパイルエラー!
fmt.Println(tempC, tempF)
// Text は string のエイリアスなので、そのまま string として使える
var msg Text = "こんにちは"
var s string = msg // ✅ 同一の型
fmt.Println(s)
}
package main
import "fmt"
// ══════════════════════════════════════
// 型定義の最大の利点:メソッドを追加できる
// ══════════════════════════════════════
type Celsius float64
// 型定義で作った型にはメソッドを定義できる
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func (c Celsius) String() string {
return fmt.Sprintf("%.1f°C", c)
}
type Fahrenheit float64
func (f Fahrenheit) String() string {
return fmt.Sprintf("%.1f°F", f)
}
func main() {
boiling := Celsius(100)
fmt.Println(boiling) // → 100.0°C
fmt.Println(boiling.ToFahrenheit()) // → 212.0°F
}
よくある間違い・注意点
1. = の有無で意味がまったく異なる
type MyInt = int // エイリアス宣言:MyInt は int と同一
type MyInt int // 型定義:MyInt は int とは別の新しい型
たった = 1文字の違いですが、挙動が大きく変わります。コードレビューの際に見落としやすいポイントです。
2. エイリアスにはメソッドを追加できない
type Text = string
// func (t Text) Upper() string { ... } // ❌ エイリアスにはメソッドを定義できない
type MyString string
func (s MyString) Upper() string { ... } // ✅ 型定義ならメソッドを追加できる
メソッドを追加したい場合は、エイリアスではなく型定義を使いましょう。
3. 型定義を使うと型安全性が高まる
type UserID int
type ProductID int
func FindUser(id UserID) { /* ... */ }
// FindUser(ProductID(1)) // ❌ ProductID を UserID として渡せない
// → 異なる概念の ID を間違えて渡すバグを防げる
まとめ
- 型宣言にはエイリアス宣言(
type A = B)と型定義(type A B)の2種類がある。 - エイリアスは元の型とまったく同一。型定義は元の型に基づく別の新しい型を作る。
- 型定義で作った型にはメソッドを追加でき、型安全性を高められる。
- 括弧
()を使って複数の型宣言をまとめて記述できる。 =の有無で意味が完全に変わるため、注意深く読み書きすること。
この後の仕様で、エイリアス宣言と型定義それぞれの詳細なルールが説明されます。まずは「2つの形式がある」という全体像をしっかり押さえておきましょう!
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント