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

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

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

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

スポンサーリンク

背景

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

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

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

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

オペランド(Operands)

オペランドは、式の中の基本的な値を表す。オペランドは、リテラル、定数・変数・関数を表す(修飾されている可能性のある)ブランクでない識別子、または括弧で囲まれた式のいずれかである。

Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .

ジェネリック関数を表すオペランド名の後には、型引数のリストを続けることができる。結果のオペランドはインスタンス化された関数となる。

ブランク識別子は、代入文の左辺においてのみオペランドとして現れることができる。

実装上の制限:空の型集合を持つ型パラメータがオペランドの型である場合、コンパイラはエラーを報告する必要はない。そのような型パラメータを持つ関数はインスタンス化できない。インスタンス化を試みると、インスタンス化の箇所でエラーが発生する。


解説

オペランドってなに?

前回「式は、オペランドに演算子や関数を適用して値を計算するもの」と学びましたね。今回はそのオペランド、つまり「計算の材料」にあたる部分の話です。

たとえば x + 42 という式があったら、x42 がオペランドで、+ が演算子です。料理に例えるなら、オペランドは食材、演算子は調理法です。

オペランドの3つの種類

オペランドになれるものは大きく分けて3つあります。

1. リテラル(Literal)

コードに直接書かれた値のことです。

42          // int リテラル
3.14        // float リテラル
1+2i        // 虚数リテラル
'A'         // rune リテラル(1文字)
"hello"     // string リテラル

リテラルにはさらに、構造体やスライスなどをその場で組み立てる複合リテラルと、関数をその場で作る関数リテラルもあります。

[]int{1, 2, 3}                  // 複合リテラル(スライス)
Point{x: 1, y: 2}              // 複合リテラル(構造体)
func(x int) int { return x*2 } // 関数リテラル(無名関数)

2. 識別子(名前)

変数、定数、関数の名前です。

x              // 変数名
math.Pi        // 修飾された識別子(パッケージ名.識別子名)
fmt.Println    // 修飾された識別子

math.Pi のようにパッケージ名がついたものを「修飾された識別子(QualifiedIdent)」と呼びます。自分のパッケージの中にある名前はそのまま、別のパッケージのものは パッケージ名.名前 という形で参照するわけですね。

3. 括弧で囲まれた式

式を () で囲んだものも、それ自体がオペランドになります。

(x + y) * z   // (x + y) がひとつのオペランドとして扱われる

算数で (2 + 3) × 4 と書くのと同じ感覚です。括弧の中を先に計算して、その結果がひとつの値(オペランド)になります。

ジェネリック関数のインスタンス化

ジェネリック関数の名前の後ろに型引数をつけると、具体的な型が確定した関数になります。

func min[T ~int|~float64](x, y T) T { ... }

// min[int] は「T が int に確定した min 関数」というオペランド
f := min[int]   // f は func(int, int) int 型の値
f(3, 5)         // 3 を返す

min だけだと「まだ型が決まっていない設計図」ですが、min[int] とすることで「int 用に完成した関数」になります。これがインスタンス化です。

ブランク識別子 _ の制限

_ はオペランドとして使えますが、代入文の左辺だけです。右辺(値として読み取る側)には使えません。

_, err := os.Open("file.txt")  // OK! 左辺で使っている
x := _                         // コンパイルエラー! 右辺では使えない

_ は「この値は捨てます」という宣言なので、「捨てたものを読み取る」のは意味がありませんよね。

空の型集合に関する実装上の制限

最後の段落はかなり特殊なケースの話です。型制約が矛盾していて、どの型も当てはまらない場合があります。

// int かつ string を同時に満たす型は存在しない → 型集合が空
type Empty interface {
    ~int
    ~string
}

func impossible[T Empty](v T) T {
    return v
}

このような関数は定義自体はエラーにならないことがありますが、呼び出そうとした瞬間にエラーになります。当てはめられる型が存在しないので、インスタンス化できないからです。実際に遭遇することはほぼないので、「こういう端っこのケースもある」程度で大丈夫です。

おわりに 

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

よっしー
よっしー

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

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

コメント

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