logo
sliceに感動した日のイメージ

先日、帰社日に実施する勉強会のためにGolangについて改めて仕様やら小ネタやらを漁りまくりました。

業務で日々使っているから〜と復習程度に考えていたらとんでもない、調べれば調べるほどまだまだ知らないことがたくさんあるんだと痛感しました。
特に今回「へえ〜〜頭いいな〜〜!」と感動した仕組みについて、勉強会でも触れたけれどブログでも書いちゃう。感動したッ!

Golangにはsliceという型が存在します。
sliceとはざっくり何かというと、「要素数が可変長の配列」型です。
「要素数が可変長の配列」という字面自体が、Goに初めて触れた当時も衝撃的だったのを覚えています。あの頑固なarrayちゃんが…?!
どんな仕組みで可変長配列を実現しているのでしょうか。

sliceの説明の前に、まず固定長配列であるarray型の性質について。
array型は宣言した際に要素数が固定で決まります。
また、他の配列に代入したり引数として関数へ渡す場合、同じ要素数でないといけません。
これはarray型が実体としてデータそのものが宣言されているからです。
データそのものということは、例えば 配列a を 配列b に代入すると、 配列a の内容をコピーしてメモリ上は別のデータである 配列b が宣言されます。
引数で渡した場合でも同じで、配列b を引数に関数を呼び出すと、関数内では別データの 配列c として扱われます。

実体であるarray型と比較して、slice型は「arrayを参照」しています。
厳密には、「長さとarrayへのポインタを持つ構造体」ですが、ここでは一旦割愛します。

sliceを宣言すると、実体データであるarrayが生成され、そのarrayをsliceが参照する、というワンクッション置いた構造となっています。
他のsliceへ代入する際や関数の引数では、この「実体arrayの参照先」を渡すことになるので、メモリ上でも同じデータとして扱われます。
逆に言えば、参照先の実体arrayとは別の、要素数が違う実体arrayにsliceの参照先を変えれば表面上「可変長」なのです。

例えば sliceA に要素を追加した場合、内部的には、
1, sliceA が参照しているのは arrayA
2, arrayAは固定長なので、arrayA と 追加したい要素の数を足した arrayB を生成
3, sliceA の参照先を arrayB に変更

という流れで処理され、結果的にsliceAは要素数が変更されたことになります。

Golangには組み込み関数 func append(slice []Type, elems …Type) []Type があり、sliceの要素追加はこの関数で行います。
append関数の引数1に元のslice、引数2以降に追加したい要素を好きなだけ。
上記の sliceA の例だと

sliceA := []int{1, 2, 3}
sliceA = append(sliceA, 4, 5)

といった具合です。
Golang公式が提供しているチュートリアル教材の『A Tour of Go』で実際にサンプルコードを試せるので興味があればGo。
サンプルコード右下の「Run」を押すと実行されます。
https://go-tour-jp.appspot.com/moretypes/15

自分の整理も兼ねて文字に起こすと理解が深まります!
次回の担当でも、日頃使っている関数やパッケージについて深掘りしてみたら楽しいかも。