update.はじめにこの記事はGoアドベントカレンダーの1日目の記事です。スライスの実態runtimeのコードをみるとGoのスライスは以下のように定義されています。int}reflectパッケージのSliceHeaderを見ても次のような定義になっています。int}つまり、Goのスライスは次の図のように、配列へのポインタと長さと容量を持った値として表現されています。runtimeとreflectパッケージでポインタがunsafe.Pointerとuintptrで表現方法は違いますが、どちらもポインタを表す値です。unsafe.Pointerは任意型のポインタと相互変換可能な型です。一方で、uintptrは整数型でunsafe.Pointerに変換が可能な型です。uintptrは整数型の1つなので、int型などと同様に四則演算が可能です。次のように、[]int型をreflect.SliceHeader型として解釈してみます。[]int型の変数であるnsのポインタを取り、unsafe.Pointer型に変換します。変換した値はptrという変数に入れています。次に、ptrを*reflect.Slice型にキャストし、そのポインタが指す先の値をreflect.SliceHeader型の変数sに代入しています。s)}要素へのアクセスreflect.SliceHeaderを用いてi番目のスライスの要素にアクセス方法を考えて見ましょう。reflect.SliceHeaderはスライスが参照している配列へのポインタを持っています。そのポインタは、スライスの0番目の要素を指すポインタです。そのため、次のat関数のように、先頭へポインタから要素i個分のポインタを進めた場所がi番目の要素のポインタとなります。要素の型によって、1要素あたりどのくらいポインタを進めれば良いのかは違うため、次の例ではint型の場合に限定しています。任意の型のサイズを取得するには、unsafe.Sizeofを用います。fmt.Println(a)}要素の追加ここでappend関数のような機能を実装してみたいと思います。appendは追加する際に、スライスが参照している配列の容量が足りている場合と足りたない場合で挙動が異なるため、それぞれ考えてみます。容量が足りる場合は、単純に次の手順で要素の追加を行います。一方で、容量が足りない場合は、次のように配列の再確保を行う必要があります。元のおよそ2倍の容量を確保しなおす配列へのポインタを貼り直す元の配列から要素をコピーする新しい要素をコピーする長さと容量を更新するこの手順でスライスのサイズを拡張するための関数growsliceを実装すると次のようになります。myAppend内で容量が足りない場合にgrowsliceを呼んでやることでスライスを拡張してやることができます。fmt.Println(ns)}ここで注意したいのは、myAppendの戻り値がreflect.SliceHeaderという点です。要素の追加が行われた場合、少なくとも長さは更新されるため、戻り値として返す必要があります。これはreflect.SliceHeader型は構造体のため、引数で受け取った値のフィールドをいくら変更しても呼び出し元に影響がないからです。次の例を見てみるとs2.Lenを変更してもs1.Lenに影響がないことが分かります。3}}おわりにこの記事では、スライスがどのように表現されているかを調べ、スライスに関数する処理を実際に実装するとことで、スライスを理解することを試みました。ここで扱ったunsafe.Pointerは次の例のように扱い方を間違えると大変危険なので、ご注意ください。artistFollowMercariフリマアプリ「メルカリ」を、グローバルで開発しています。upLoginユーザーは見つかりませんでした。