Go言語: スライスの構造

# この記事について
Go言語のスライスについて、その内部構造を整理します。

# まとめ
- スライスとは、配列をラップして使いやすくした型
  - スライスと、配列の実体はメモリ上では別のオブジェクト
- スライスは以下の3つのパラメータを持つ構造体
  - 配列の実体へのポインター
  - 配列のうち、そのスライスで使用可能なサイズ (Len)
  - 配列の実際のサイズ (Cap)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/f9a1d27b-1224-4f22-bbcb-7eb3c6eb875b.png" />

- `reflect.SliceHeader` を用いてスライスの構造を見ることが出来る

```go
    type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
    }
```

# 説明

### スライスの作成

まずは、スライス(Len = 5, Cap = 8 )を作成してみる。

```go
    a := make([]byte, 5, 8)
```

こんな構造になる。
(Pointer の値 0xc00000a0d0 は今回試した際に確保された配列のアドレス)

|![slice0010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/e3f2d50c-1e7f-6ce0-aea0-70e314906485.png)|
|:-:|

なお、Capが余っていても Len より大きい要素にはアクセスできない。アクセスすると実行時に `panic: runtime error: index out of range [5] with length 5` というパニックが起きる。


```go
     a[0] = 0x10
     a[1] = 0x11
     a[2] = 0x12
     a[3] = 0x13
     a[4] = 0x14
     //a[5] = 0x15 → これは実行時にパニック
```

### スライシング

`a` をスライシングしてスライス `b` を生成してみる。

```go
    b := a[1:4]
```

この場合、スライス `b` は次のような構造になる。

|![slice0020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/ccebfdc1-e39f-63ed-a423-2b9a6222d4c6.png)|
|:-:|

スライシングのポイントは以下。

- 配列自体はコピーされない
- スライス `a`, `b` は同じ配列を参照する
  - ポインター、Len、Cap が異なってるだけ
- なので `a` のデータを変更すると `b` にも影響がある

一応、スライス `a`, `b` を１つの図にするとこんな感じ。

|![slice0025.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/2fd7b53c-62a3-e91c-67e4-1a846790bcac.png)|
|:-:|


### var で定義したスライス

以下のように宣言すると、Pointer = 0, Len = 0, Cap = 0 というスライスになる。つまりこの時点ではスライス変数は存在しても、配列は存在していない。

```go
    var c []byte
```

こういう状態。

|![slice0031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/7a46b2b8-e464-40b0-93b4-044d847b00c1.png)|
|:-:|

このスライスに対して append() 関数を呼んで要素を追加することで配列が確保される。また、必要に応じて配列が拡張される。

```go
    for i :=0; i < 100; i++ {
        c = append(c, byte(i))
    }
```

上記コードを実行した後は、例えば以下のようになる。
(今回試した際は、Len=100、Cap=128になった)。

|![slice0030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/67fe2abb-7718-e748-0774-ed477268c3e6.png)|
|:-:|

### スライス `var c []byte` と `c := []byte{}` の違い

`var c []byte` は先に見たように配列を指してないが、`c := []byte{}` は Len, Cap ともに 0 であるものの、どこかのアドレスを指している(配列のメモリが確保されている？)。

|![slice0040.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539081/2f9c3315-84cb-c0fa-d24e-2487f69ad143.png)|
|:-:|

これでどんな違いが出るかというと、例えば json の Encoder を通すと前者は null、後者は空文字として出力される。

```go
    var c1 []byte    // 配列を指してない
    c2 := []byte{}   // どこかのアドレスを指している

    buf1 := new(bytes.Buffer)
    buf2 := new(bytes.Buffer)
    json.NewEncoder(buf1).Encode(&c1)
    json.NewEncoder(buf2).Encode(&c2)
    fmt.Printf("var c1 []byte  ===> %s", buf1.String())
    fmt.Printf("c2 := []byte{} ===> %s", buf2.String())
```

出力結果：

```
var c1 []byte  ===> null
c2 := []byte{} ===> ""
```


### スライス変数の中身

スライスは len() と cap() 関数でそれぞれ Length と Capacity を取得できるが、`reflect.SliceHeader` に変換することで中身を見ることもできる。

reflect.SliceHeader はこんな構造。

```
type SliceHeader struct {
    Data uintptr  // これが配列のアドレス
    Len  int
    Cap  int
}
```

以下のように unsafe.Pointer を経由して、スライスを reflect.SliceHeader に変換できる。

```go
    d := make([]byte, 5, 8)
    uptr := unsafe.Pointer(&d)
    hdr := (*reflect.SliceHeader)(uptr)
    fmt.Printf("Data: %x, Len: %d, Cap: %d\n", hdr.Data, hdr.Len, hdr.Cap) 
```

実行例： `Data: c000079ee0, Len: 5, Cap: 8`


# 参考

- [Go Slices: usage and internals](https://blog.golang.org/slices-intro)