
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
短縮変数宣言(Short variable declarations)
短縮変数宣言は以下の構文を使用する:
ShortVarDecl = IdentifierList ":=" ExpressionList .
これは、初期化式を持つが型を持たない通常の変数宣言の省略形である:
"var" IdentifierList "=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe() // os.Pipe() は接続されたファイルのペアと、もしあればエラーを返す
_, y, _ := coord(p) // coord() は3つの値を返す; y座標のみに関心がある
通常の変数宣言とは異なり、短縮変数宣言は、同じブロック内(ブロックが関数本体の場合はパラメータリスト内)で以前に同じ型で宣言された変数を再宣言することができる。ただし、ブランクでない変数のうち少なくとも1つが新しい変数でなければならない。この結果、再宣言は複数変数の短縮宣言においてのみ現れることができる。再宣言は新しい変数を導入しない。元の変数に新しい値を代入するだけである。:= の左辺のブランクでない変数名は一意でなければならない。
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // offset を再宣言する
x, y, x := 1, 2, 3 // 不正: := の左辺で x が繰り返されている
短縮変数宣言は関数の内部でのみ使用できる。if、for、switch 文の初期化子のような一部の文脈では、ローカルな一時変数を宣言するために使用できる。
解説
:= は var の省略形
Goでは変数を宣言するとき、毎回 var と書くのは少し面倒ですよね。そこで用意されているのが := を使った短縮変数宣言です。
// この2つはまったく同じ意味です
var name = "Alice"
name := "Alice"
:= を使うと、型の指定を省略して、右辺の値からコンパイラに型を推論してもらえます。Go のコードでは、関数の中ではこちらの書き方のほうが圧倒的によく使われます。
使えるのは関数の中だけ
ひとつ大事な制限があります。:= は関数の中でしか使えません。パッケージレベル(関数の外)では必ず var を使う必要があります。
package main
name := "Alice" // コンパイルエラー! 関数の外では := は使えない
func main() {
name := "Alice" // OK! 関数の中なので大丈夫
}
再宣言のルール
:= には var にはない特別な機能があります。それが再宣言です。
たとえば、エラー処理で何度も err を受け取る場面を想像してください。
file, err := os.Open("data.txt") // file と err を新しく宣言
data, err := io.ReadAll(file) // data は新しく宣言、err は再宣言(上書き)
2行目の err はすでに存在するので、新しい変数を作るのではなく、既存の err に新しい値を代入するだけです。これが再宣言です。
ただし、以下の条件をすべて満たす必要があります。
- 同じブロック内で以前に宣言された変数であること
- 同じ型であること
:=の左辺に、少なくとも1つは新しい変数が含まれていること
3つ目の条件が特に重要です。すべての変数が既存のものだと、:= ではなく =(普通の代入)を使うべきだからです。
offset := 0
offset := 10 // コンパイルエラー! 新しい変数がひとつもない。= を使うべき
同じ名前の繰り返しは禁止
:= の左辺で同じ変数名を2回以上書くことはできません。
x, y, x := 1, 2, 3 // コンパイルエラー! x が2回出ている
これは単純に「どっちの x に何を入れるの?」が曖昧になるからですね。
if や for の中での活用
:= は if、for、switch の初期化部分でもよく使われます。こうすると、そのブロックの中だけで有効な一時変数を簡潔に作れます。
// if の初期化子で err を宣言。err は if ブロックの中だけで有効
if err := doSomething(); err != nil {
fmt.Println("エラー:", err)
}
// ここでは err は見えない
// for の初期化子で i を宣言
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// ここでは i は見えない
この書き方のいいところは、変数のスコープ(有効範囲)が最小限に抑えられることです。使い終わった変数がいつまでも残らないので、コードが読みやすくなりますし、うっかり別の場所で同じ変数を使ってしまうミスも防げます。
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント