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

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

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

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

スポンサーリンク

背景

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

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

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

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

clear(Clear)

組み込み関数 clear は、map、スライス、または型パラメータ型の引数を取り、すべての要素を削除またはゼロ化する [Go 1.21]。

呼び出し     引数の型          結果

clear(m)    map[K]T           すべてのエントリを削除し、
                              空のmapにする(len(m) == 0)

clear(s)    []T               s の長さまでのすべての要素を
                              T のゼロ値に設定する

clear(t)    型パラメータ       以下を参照

clear の引数の型が型パラメータの場合、その型集合のすべての型はmapまたはスライスでなければならず、clear は実際の型引数に対応する操作を実行する。

mapまたはスライスが nil の場合、clear は何もしない。


解説

clear ってなに?

clear は Go 1.21 で追加された組み込み関数で、スライスやmapの中身を一括でリセットする機能を提供します。

m := map[string]int{"a": 1, "b": 2, "c": 3}
clear(m)
fmt.Println(m)       // map[]
fmt.Println(len(m))  // 0

map に対する clear:全エントリ削除

map に対して clear を使うと、すべてのキーと値が削除され、空のmapになります。

m := map[string]int{"x": 1, "y": 2, "z": 3}
clear(m)
fmt.Println(len(m))  // 0
fmt.Println(m)       // map[]

clear の前は、ループで1つずつ delete するか、新しいmapを作り直す必要がありました。

// clear 以前の方法1:ループで削除
for k := range m {
    delete(m, k)
}

// clear 以前の方法2:新しいmapに置き換え
m = make(map[string]int)

// Go 1.21 以降:一行で済む
clear(m)

clearmake で新規作成する方法の違いは、同じmapオブジェクトを再利用するかどうかです。clear は既存のmapをそのまま使い回すので、そのmapへの参照を持つ他の変数にも「空になった」ことが反映されます。

m1 := map[string]int{"a": 1}
m2 := m1      // m2 は m1 と同じmapを参照

clear(m1)
fmt.Println(m2)  // map[](m2 にも反映される)

// make だと別のmapになる
m1 = make(map[string]int)
m1["b"] = 2
fmt.Println(m2)  // map[](m2 は古いmapを指したまま)

スライスに対する clear:ゼロ値で埋める

スライスに対する clear の動作は、mapとは少し異なります。要素を削除するのではなく、すべての要素をゼロ値に設定します。長さや容量は変わりません。

s := []int{1, 2, 3, 4, 5}
clear(s)
fmt.Println(s)       // [0 0 0 0 0]
fmt.Println(len(s))  // 5(長さは変わらない)
fmt.Println(cap(s))  // 5(容量も変わらない)

ポインタや参照型の要素を含むスライスでは、clear がゼロ値(nil)を設定することでガベージコレクションの対象になるので、メモリリークの防止にも役立ちます。

type Data struct {
    buf []byte
}

s := []*Data{{buf: make([]byte, 1000)}, {buf: make([]byte, 1000)}}
s = s[:0]     // 長さを0にしても、基底配列の要素はまだポインタを保持している
// clear を使えばポインタが nil になり、Data がGC対象になる

nil に対しては何もしない

nil のmapやスライスに対して clear を呼んでも、パニックしません。単に何もしません。

var m map[string]int  // nil
clear(m)              // 何も起きない(パニックしない)

var s []int           // nil
clear(s)              // 何も起きない

安全に呼び出せるので、nil チェックを事前に入れる必要はありません。

map と スライスでの動作の違いに注意

この違いは混同しやすいので整理しておきましょう。

対象動作長さの変化
map全エントリを削除len が 0 になる
スライス全要素をゼロ値に設定len は変わらない

mapは「中身を空にする」、スライスは「中身をリセットする」と覚えるとわかりやすいです。スライスの長さも0にしたい場合は、別途 s = s[:0] と書く必要があります。

おわりに 

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

よっしー
よっしー

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

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

コメント

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