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

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

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

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

スポンサーリンク

背景

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

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

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

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

関数宣言(Function declarations)

関数宣言は、識別子(関数名)を関数に束縛する。

FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

関数のシグネチャが結果パラメータを宣言している場合、関数本体の文リストは終端文で終わらなければならない。

func IndexRune(s string, r rune) int {
	for i, c := range s {
		if c == r {
			return i
		}
	}
	// 不正: return文が欠けている
}

関数宣言が型パラメータを指定している場合、その関数名はジェネリック関数を表す。ジェネリック関数は、呼び出されたり値として使用されたりする前に、インスタンス化されなければならない。

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

型パラメータを持たない関数宣言は、本体を省略することができる。このような宣言は、アセンブリルーチンなど、Goの外部で実装された関数のシグネチャを提供する。

func flushICache(begin, end uintptr)  // 外部で実装されている

解説

関数宣言の基本

Goで関数を作るには func キーワードを使います。基本の形はこうです。

func 関数名(引数) 戻り値の型 {
    // 処理
}

具体例を見てみましょう。

func Add(a int, b int) int {
    return a + b
}

これで「Add という名前の、int を2つ受け取って int を1つ返す関数」が宣言できました。

戻り値があるなら必ず return で終わること

原文の IndexRune の例は、「戻り値の型を宣言しているのに、return で終わらないパスがある」というやってはいけない例です。

func IndexRune(s string, r rune) int {
    for i, c := range s {
        if c == r {
            return i   // 見つかったら返す
        }
    }
    // ループが終わってもどこにも return がない → コンパイルエラー!
}

for ループの中で見つからなかった場合、関数はどの値を返せばいいのかわかりません。正しくはこうです。

func IndexRune(s string, r rune) int {
    for i, c := range s {
        if c == r {
            return i
        }
    }
    return -1  // 見つからなかった場合のデフォルト値
}

「すべての道が return にたどり着くか?」をコンパイラがチェックしてくれるので、戻り値の返し忘れを防げます。

ジェネリック関数

関数名の後ろに [T ...] のような型パラメータをつけると、ジェネリック関数になります。前回までに学んだ型制約の知識がここで活きてきますね。

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

この関数は「基底型が int または float64 であるどんな型でも使える min 関数」です。

min(3, 5)       // T は int と推論される → 3 を返す
min(2.7, 1.3)   // T は float64 と推論される → 1.3 を返す

型パラメータを明示的に書くこともできます。

min[int](3, 5)  // 「T は int だよ」と明示する書き方

原文にある「インスタンス化」とは、この型パラメータに具体的な型を当てはめることです。ジェネリック関数はいわば「型の穴が空いた設計図」なので、使うときにはその穴を埋める必要がある、というイメージですね。

本体のない関数宣言

これはちょっと珍しいケースですが、関数の本体({...} の部分)を省略できる場合があります。

func flushICache(begin, end uintptr)  // 本体がない!

「本体がなかったら何も実行できないのでは?」と思いますよね。実はこれ、処理の本体がGoではなく、アセンブリ言語などで別途書かれていることを意味しています。

Goの標準ライブラリの内部では、パフォーマンスが極めて重要な低レベル処理(CPUキャッシュの操作、暗号演算など)にこのパターンが使われています。普段のアプリケーション開発で使うことはまずないので、「こういう書き方もあるんだな」と知っておくだけで十分です。

おわりに 

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

よっしー
よっしー

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

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

コメント

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