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

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

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

本日は、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 が繰り返されている

短縮変数宣言は関数の内部でのみ使用できる。ifforswitch 文の初期化子のような一部の文脈では、ローカルな一時変数を宣言するために使用できる。


解説

:= は 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. 同じブロック内で以前に宣言された変数であること
  2. 同じ型であること
  3. := の左辺に、少なくとも1つは新しい変数が含まれていること

3つ目の条件が特に重要です。すべての変数が既存のものだと、:= ではなく =(普通の代入)を使うべきだからです。

offset := 0
offset := 10  // コンパイルエラー! 新しい変数がひとつもない。= を使うべき

同じ名前の繰り返しは禁止

:= の左辺で同じ変数名を2回以上書くことはできません。

x, y, x := 1, 2, 3  // コンパイルエラー! x が2回出ている

これは単純に「どっちの x に何を入れるの?」が曖昧になるからですね。

if や for の中での活用

:=ifforswitch の初期化部分でもよく使われます。こうすると、そのブロックの中だけで有効な一時変数を簡潔に作れます。

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

よっしー
よっしー

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

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

コメント

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