Go言語入門:言語仕様 -Vol.49-

スポンサーリンク
Go言語入門:言語仕様 -Vol.49- 用語解説
Go言語入門:言語仕様 -Vol.49-
この記事は約6分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、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言語の言語仕様について解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました