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

スポンサーリンク
Introduction to Go language: Language specification -Vol.149- 用語解説
Introduction to Go language: Language specification -Vol.149-
この記事は約9分で読めます。
よっしー
よっしー

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

本日は、Go言語の言語仕様について解説しています。

スポンサーリンク

背景

Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。

言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。

そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。

言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!

付録

言語のバージョン

Go 1 互換性保証は、Go 1 仕様に従って書かれたプログラムが、その仕様の存続期間にわたって、変更されることなくコンパイルされ正しく動作し続けることを保証します。より一般的に言えば、言語に調整が加えられたり機能が追加されたりしても、互換性保証によって、特定のGo言語バージョンで動作するGoプログラムは、それ以降のどのバージョンでも動作し続けることが保証されます。

たとえば、2進整数リテラルに接頭辞 0b を使える機能はGo 1.13で導入され、整数リテラルの節で [Go 1.13] と示されています。0b1011 のような整数リテラルを含むソースコードは、コンパイラが使用する暗黙的または要求される言語バージョンがGo 1.13より古い場合、拒否されます。

次の表は、Go 1 以降に導入された機能に必要な最小の言語バージョンを示しています。

Go 1.9

Go 1.13

  • 整数リテラルで、2進数・8進数のために接頭辞 0b0B0o0O を使えます。
  • 16進浮動小数点リテラルを接頭辞 0x0X を使って書けます。
  • 虚数接尾辞 i を、10進リテラルだけでなく、任意の(2進・10進・16進の)整数または浮動小数点リテラルに使えます。
  • 任意の数値リテラルの数字を、アンダースコア _ を使って区切る(グループ化する)ことができます。
  • シフト演算におけるシフト数(シフトカウント)に、符号付き整数型を使えます。

Go 1.14

Go 1.17

  • スライスと配列の要素型が一致し、配列がスライスより長くない場合、スライスを配列ポインタに変換できます。
  • 組み込みパッケージ unsafe に、新しい関数 AddSlice が含まれます。

Go 1.18

1.18 リリースは、多相的な関数と型(「ジェネリクス」)を言語に追加します。具体的には次のとおりです。

  • 演算子と区切り記号の集合に、新しいトークン ~ が含まれます。
  • 関数宣言と型宣言で型パラメータを宣言できます。
  • インターフェース型は、(インターフェースの型名だけでなく)任意の型を埋め込むことができ、またユニオンや ~T 型要素も埋め込めます。
  • 事前宣言された型の集合に、新しい型 anycomparable が含まれます。

Go 1.20

  • スライスと配列の要素型が一致し、配列がスライスより長くない場合、スライスを配列に変換できます。
  • 組み込みパッケージ unsafe に、新しい関数 SliceDataStringStringData が含まれます。
  • 比較可能な型(通常のインターフェースなど)は、型引数が厳密には比較可能でなくても、comparable 制約を満たせます。

Go 1.21

  • 事前宣言された関数の集合に、新しい関数 minmaxclear が含まれます。
  • 型推論が、推論のためにインターフェースメソッドの型を使います。また、変数に代入されたり、他の(ジェネリックかもしれない)関数に引数として渡されたりするジェネリック関数についても、型引数を推論します。

Go 1.22

  • 「for」文において、各反復(イテレーション)は、毎回同じ変数を共有するのではなく、それぞれ独自の反復変数の集合を持ちます。
  • 「range」節を持つ「for」文で、0 から上限までの整数値を反復できます。

Go 1.23

  • 「range」節を持つ「for」文が、range 式としてイテレータ関数を受け付けます。

Go 1.24

解説

結論:この付録は「Go 1 互換性保証」と「機能ごとの導入バージョン一覧」を述べている。互換性保証とは『古いコードは新しいGoでもそのまま動く』という約束。一覧表は、各機能がどのバージョンから使えるようになったかの早見表。

Go 1 互換性保証とは

この付録の前半が説明しているのは、Goという言語の最大の魅力の1つ、後方互換性の保証です。

意味はシンプルです。**「Go 1 のルールで書いたプログラムは、その後どれだけGoが進化しても、書き直すことなくコンパイル・実行できる」**という約束です。

これがどれほど重要か、他のソフトウェアに例えるとわかります。多くの言語やライブラリでは、バージョンが上がると古い書き方が使えなくなり(「破壊的変更」)、コードの修正を強いられます。Goはこれを極力避けると公式に約束しています。だから、何年も前に書いたGoのコードが、最新のGoでもそのまま動きます。

家の規格に例えると、「一度建てた家は、その後に建築基準法が新しくなっても、建て直しを求められない」という保証のようなものです。安心して長く使い続けられます。

バージョンと「拒否される」とは

原文の 0b1011(2進数リテラル)の例が、互換性保証の逆方向の注意点を示しています。

互換性保証は「古いコードが新しいGoで動く」ことを保証しますが、その逆、つまり「新しい機能を使ったコードが古いGoで動く」ことは保証しません。

x := 0b1011 // 2進数で 11。Go 1.13 以降でしか書けない

この 0b(2進数の接頭辞)はGo 1.13で追加された機能です。だからGo 1.12 以前のコンパイラでこのコードを処理しようとすると拒否(エラー)されます。新しい機能は、それが導入されたバージョン以降でないと使えない、という当たり前の制約です。

カレンダーに例えると、「2020年に発明された道具は、2019年には存在しないので使えない」というだけのことです。

仕様書中の [Go 1.13] 表記の意味

ここで、これまでの節で時々登場した [Go 1.17] [Go 1.20] といった角括弧の表記の正体が明かされます。前の unsafe の節で AddSlice[Go 1.17] が付いていたのを覚えているでしょうか。

あれは**「この機能はGoのこのバージョンで追加されました」という印**だったのです。つまり、その印より古いGoではその機能は使えません。仕様書はこうして、各機能の「いつから使えるか」を明示しています。

バージョン別の機能一覧の読み方

後半の表は、Go 1 以降に追加された主な機能を、導入バージョンごとにまとめた早見表です。これを丸暗記する必要はまったくありません。「Goはこういう進化をたどってきた」という歴史と、「この機能はいつから使えるか」を調べたいときの参照先として捉えてください。

初心者にとって特に知っておく価値のある、重要なものだけをかいつまんで説明します。

Go 1.13:数値リテラルの書きやすさ向上

2進数(0b)・8進数(0o)・16進数の表記が整理され、さらに数字をアンダースコアで区切れるようになりました。

million := 1_000_000   // 桁区切りに _ を使える(読みやすい)
binary  := 0b1010_1010 // 2進数も _ で区切れる

_ は単なる見やすさのための区切りで、値には影響しません。大きな数を扱うときに便利です。

Go 1.18:ジェネリクス(generics)の導入 ← 最重要

これはGoの歴史上、最も大きな機能追加です。**ジェネリクス(総称型)とは、「型を後から差し替えられる、汎用的な関数や型」**を書く仕組みです。

それ以前は、int 用とstring 用で同じ処理を別々に書く必要がありましたが、ジェネリクスで1つにまとめられるようになりました。

// 型 T を後から指定できる、汎用的な関数
func Max[T int | float64](a, b T) T {
	if a > b {
		return a
	}
	return b
}

Max(3, 5)       // int として使える
Max(2.5, 1.8)   // float64 としても使える

このとき同時に追加された any(あらゆる型を表す。interface{} の別名)や comparable(比較可能な型を表す)、新トークン ~ も、ジェネリクスを支える要素です。any は実務で非常によく見かけます。

Go 1.21:便利な組み込み関数 minmaxclear

それまで自分で書く必要があった最小値・最大値の取得が、組み込み関数として用意されました。

min(3, 5)    // 3
max(3, 5)    // 5
clear(myMap) // マップの中身を空にする

Go 1.22:for ループの落とし穴の修正

これは地味ですが重要な変更です。それ以前のGoでは、for ループの変数が毎回使い回される仕様で、ループ内でゴルーチンを起動すると予期しない動作をする、有名な罠がありました。Go 1.22で毎回の反復が独自の変数を持つように改められ、この罠が解消されました。

また、整数を直接 range で回せるようにもなりました。

for i := range 5 { // 0,1,2,3,4 と回る(Go 1.22以降)
	fmt.Println(i)
}

この付録から得るべきこと

細かい一覧を覚える必要はありません。実用的に押さえるべきは次の3点です。

第一に、Goには強力な後方互換性保証があり、古いコードは安心して使い続けられること。第二に、新機能はそれが導入されたバージョン以降でないと使えないので、チームやプロジェクトのGoバージョンを意識する必要があること。第三に、歴史上の最重要アップデートは Go 1.18のジェネリクスであり、それに伴う any などは現代のGoコードで普通に登場すること。この3点を理解しておけば十分です。


補足:これでGo言語仕様書(The Go Programming Language Specification)を、付録まで含めて最後まで読み終えたことになります。長い道のりお疲れさまでした。仕様書は「言語の厳密なルールブック」なので硬い記述が多かったはずですが、ここまで通読したことで、Goという言語の設計思想——型安全・並行処理・後方互換性・シンプルさ——の全体像がつかめたと思います。次のステップとしては、学んだ仕様を実際のコードで確かめること、標準ライブラリ(fmtnet/httpencoding/jsonsync など)に触れること、そして小さくても動くプログラムを自分で書いてみることを強くおすすめします。仕様の知識は、手を動かして初めて血肉になります。

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

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

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

コメント

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