
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
Source code representation(ソースコードの表現)
ソースコードは、UTF-8でエンコードされたUnicodeテキストです。テキストは正規化されないため、単一のアクセント付きコードポイントは、アクセントと文字を組み合わせて構成された同じ文字とは区別されます。これらは2つのコードポイントとして扱われます。簡略化のため、このドキュメントでは、ソーステキスト内のUnicodeコードポイントを指すために、限定しない用語「文字(character)」を使用します。
各コードポイントは区別されます。たとえば、大文字と小文字は異なる文字です。
実装上の制限: 他のツールとの互換性のため、コンパイラはソーステキスト内のNUL文字(U+0000)を許可しない場合があります。
実装上の制限: 他のツールとの互換性のため、コンパイラは、UTF-8でエンコードされたバイトオーダーマーク(U+FEFF)がソーステキストの最初のUnicodeコードポイントである場合、それを無視する場合があります。バイトオーダーマークは、ソース内の他の場所では許可されない場合があります。
解説
ソースコードは「文字」でできている
Go言語のプログラムは、私たちが読み書きできる文字で書かれています。この「文字」がどのようにコンピュータ内部で扱われているかを理解することが、この章のテーマです。
たとえ話: 本を読むとき、日本語、英語、絵文字など、さまざまな文字が使われています。コンピュータがこれらの文字を正しく理解するためには、「文字のルール」が必要です。それがUnicodeとUTF-8です。
UTF-8とUnicodeって何?
Unicode(ユニコード)
Unicodeは、世界中のあらゆる文字(日本語、中国語、アラビア語、絵文字など)に番号を割り当てた国際規格です。
たとえば:
A→ U+0041あ→ U+3042😀→ U+1F600
この番号のことをコードポイントと呼びます。
UTF-8
UTF-8は、Unicodeのコードポイントを実際のバイト列(コンピュータが扱えるデータ)に変換する方式です。英数字は1バイト、日本語は3バイト、絵文字は4バイトといった可変長で効率的にデータを保存できます。
なぜUTF-8を使うの?
Go言語のソースコードはUTF-8で書くことが決められています。これにより、世界中のどんな言語でも変数名やコメントに使えます。
package main
import "fmt"
func main() {
// 日本語の変数名も使える!
名前 := "太郎"
fmt.Println(名前)
// 絵文字も使える!
emoji := "🎉"
fmt.Println(emoji)
}
「正規化されない」とはどういうこと?
Unicodeには、見た目が同じでも内部表現が異なる文字が存在します。
具体例: アクセント付き文字
たとえば、フランス語の「é」(eにアクセント記号)は、2通りの表現方法があります:
- 合成済み文字:
é(U+00E9) – 1つのコードポイント - 分解された文字:
e(U+0065) + ´(U+0301) – 2つのコードポイントを組み合わせる
見た目は同じ「é」ですが、Goではこれらは別の文字として扱われます。
package main
import "fmt"
func main() {
// 合成済み文字(1コードポイント)
s1 := "café"
// 分解された文字(eとアクセント記号が別々)
s2 := "café" // 見た目は同じだが内部表現が違う
fmt.Println(s1 == s2) // false になる可能性がある
fmt.Println(len(s1), len(s2)) // バイト数が異なる
}
なぜ正規化しないの?
多くのプログラミング言語では、これらを同じ文字に変換(正規化)しますが、Goはあえて変換しません。これは処理速度を優先するためです。正規化には計算コストがかかるので、Goは「書いたまま」を尊重します。
実用上のアドバイス: 日本語や英語だけを使っている限り、この問題に遭遇することはほとんどありません。ただし、ヨーロッパ言語を扱うときは注意が必要です。
「各コードポイントは区別される」
これは非常にシンプルな原則です。大文字と小文字は別の文字として扱われます。
package main
func main() {
var Name string = "太郎"
var name string = "花子"
// Name と name は完全に別の変数
fmt.Println(Name) // 太郎
fmt.Println(name) // 花子
}
同じように、全角と半角も別の文字です:
var a = "A" // 半角A (U+0041)
var b = "A" // 全角A (U+FF21)
// aとbは異なる
実装上の制限1: NUL文字(U+0000)
NUL文字は、バイナリデータで「データの終わり」を示すために使われる特殊な文字です。
Goのコンパイラは、ソースコード内にNUL文字があるとエラーにする場合があります。これは他のツール(エディタやバージョン管理システム)との互換性のためです。
// これはエラーになる可能性がある
var str = "Hello\x00World" // \x00 はNUL文字
実用上の影響: 通常のプログラミングでNUL文字をソースコードに書くことはないので、気にする必要はありません。ただし、バイナリデータを扱うときは文字列リテラルではなく、バイト配列を使いましょう。
// バイト配列ならNULも扱える
data := []byte{0x48, 0x00, 0x69} // H, NUL, i
実装上の制限2: バイトオーダーマーク(BOM)
BOM(Byte Order Mark) は、UTF-8ファイルの先頭に付けられる特殊なマーカー(U+FEFF)です。一部のWindowsツールが自動的に追加します。
BOMとは何か?
もともとBOMは、UTF-16やUTF-32で「バイトの並び順」を示すために使われますが、UTF-8には本来不要です。しかし、Windowsのメモ帳などが「このファイルはUTF-8ですよ」という目印として付けることがあります。
Goでの扱い
Goコンパイラは:
- ファイルの先頭にあるBOMは無視してくれる(親切)
- ファイルの途中にあるBOMは許可しない場合がある
// ファイルの先頭
// (BOM: EF BB BF) ← これは無視される
package main
func main() {
// ...
}
実用上のアドバイス:
- エディタの設定を確認: VSCodeやGoLandなどのモダンなエディタは、デフォルトで「UTF-8(BOMなし)」で保存します。
- もし「unexpectedな文字エラー」が出たら、ファイルにBOMが含まれていないか確認しましょう。
# BOMの有無を確認(Linux/Mac)
file mycode.go
# BOMを削除
sed -i '1s/^\xEF\xBB\xBF//' mycode.go
まとめ: ソースコード表現で覚えておくべきこと
- GoのソースコードはUTF-8で書く → 日本語や絵文字も使える
- 正規化されない → 見た目が同じでも内部表現が違う文字は別物
- 大文字・小文字は区別される →
Nameとnameは別の変数 - NUL文字は使えない → 通常は気にしなくてOK
- BOMは先頭なら無視される → エディタ設定で「BOMなし」にしておくのがベスト
これらのルールは、普段のコーディングではあまり意識する必要はありません。ただし、特殊な文字を扱ったり、国際化対応をする場合には重要になります。基本的にはUTF-8(BOMなし)で保存されたテキストファイルとして扱えば問題ありません!
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント