
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
プログラムの初期化と実行
ゼロ値
変数に対して、宣言や new の呼び出しによって記憶領域が確保されたとき、あるいは複合リテラルや make の呼び出しによって新しい値が生成されたときに、明示的な初期化が与えられていない場合、その変数や値にはデフォルト値が与えられます。そうした変数や値の各要素は、その型のゼロ値に設定されます。すなわち、ブール型には false、数値型には 0、文字列には ""、ポインタ・関数・インターフェース・スライス・チャネル・マップには nil が設定されます。この初期化は再帰的に行われます。したがって、たとえば構造体の配列の各要素は、値が指定されていなければ、そのフィールドがゼロ化されます。
次の2つの単純な宣言は等価です。
var i int
var i int = 0
以下のコードのあと、
type T struct { i int; f float64; next *T }
t := new(T)
次のことが成り立ちます。
t.i == 0
t.f == 0.0
t.next == nil
同じことは、次のコードのあとでも成り立ちます。
var t T
解説
結論:Goでは、初期値を書かずに変数を宣言しても未定義(ゴミ値)にはならず、型ごとに決まった「ゼロ値」が必ず入る。数値は0、文字列は空文字、bool は false、ポインタ等は nil。
ゼロ値とは
多くの言語では、初期値を指定せずに変数を作ると「何が入っているか分からない(ゴミ値)」状態になり、バグの温床になります。Goはこれを禁止し、初期値を書かなくても型ごとに決まった安全な初期値を必ず入れる仕組みになっています。これが「ゼロ値(zero value)」です。
var i int // i は 0
var s string // s は "" (空文字列)
var b bool // b は false
引っ越し直後の冷蔵庫に例えると、何も入れていなくても「空っぽ」という決まった状態になっている、というイメージです。中身が「何だか分からないゴミ」になることはありません。
型ごとのゼロ値一覧
| 型のグループ | ゼロ値 |
|---|---|
| ブール型(bool) | false |
| 数値型(int, float64 など) | 0(小数なら 0.0) |
| 文字列(string) | ""(空文字列) |
| ポインタ、関数、インターフェース、スライス、チャネル、マップ | nil |
nil(ニル)は「何も指していない・空っぽ」を表す特別な値です。これらの型は「中身への参照を持つ」性質があり、まだ何も参照していない状態が nil だと理解してください。
初期値の省略と明示は等価
原文の例が示すとおり、次の2行は完全に同じ意味です。
var i int // ゼロ値 0 が入る
var i int = 0 // 明示的に 0 を入れる
int のゼロ値は 0 なので、= 0 を書いても書かなくても結果は同じです。だからGoでは、ゼロ値でよい場合は初期値を省略するのが一般的です。
「再帰的に」ゼロ化される
原文の “recursively”(再帰的に)は、初心者にはイメージしづらい部分なので補足します。
これは、構造体や配列のように「入れ物の中にさらに要素がある」場合、その中身まで全部ゼロ値で埋められるという意味です。表面だけでなく、中身の隅々まで初期化されると考えてください。
原文の構造体の例で確認します。
type T struct {
i int // 整数
f float64 // 小数
next *T // T へのポインタ
}
t := new(T) // T を1つ作る(初期値の指定なし)
new(T) は T 型の領域を確保して、そのポインタを返します。初期値を指定していないので、t が指す中身は各フィールドがゼロ値で埋まります。
t.i == 0 // int のゼロ値
t.f == 0.0 // float64 のゼロ値
t.next == nil // ポインタのゼロ値
構造体 T という箱の中に i・f・next という3つの仕切りがあり、その一つひとつがそれぞれの型のゼロ値で埋められています。これが「再帰的」の意味です。もしフィールドがさらに別の構造体だったら、そのまた中身まで掘り下げてゼロ化されます。
new(T) と var t T は同じ結果
原文の最後が示すのは、書き方が違ってもゼロ値で初期化される結果は同じということです。
t := new(T) // ポインタ経由で作る
var t T // 値として直接作る
この2つは「t がポインタか値か」という違いはありますが、中身のフィールドがすべてゼロ値になる点は共通です。どちらの作り方でも、Goが責任を持って安全な初期値を入れてくれます。
補足:new と make は混同しやすいので区別しておきます。new(T) はあらゆる型に使えてゼロ値で初期化したポインタを返します。一方 make はスライス・マップ・チャネルの3種類専用で、これらを「使える状態」に初期化するための特別な関数です。この違いは別の節で詳しく扱われます。
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント