KDOC 188: 『Visual Guide to Slices in Go』
この文書のステータス
- 作成
- 2024-06-08 貴島
- レビュー
- 2024-06-12 貴島
概要
Visual Guide to Slices in Go — Ozan Sazakは、Goのスライスを解説した記事。
メモ
- スライスは配列を抽象化したもの。扱うときに実際に割り当てられているメモリについて考えなくてよくするため。たとえばCでは、当初のメモリ割当よりも配列が大きくなった場合、手動でメモリの再割り当てとコピーが必要であった
https://github.com/kd-collective/go/blob/b8ac61e6e64c92f23d8cf868a92a70d13e20a124/src/runtime/slice.go#L15-L19
type slice struct { array unsafe.Pointer len int cap int }
- capは基底配列の長さ。再メモリ確保せずに保存できる領域の長さ
- arrayフィールドは、基底配列への(最初の要素の)ポインタ。
- forによるイテレーションは、要素の長さ(len)で決まり、capの長さではない
- len == cap の状態で要素を加えた(append)とき、メモリを再割り当て(reallocate)する。capacityを増やして新しくsliceを作り、中身をコピーする
sl = append(sl, "hello")
のようになるのは、コピーによって異なるポインタを指すことがあるから- printfフォーマットの
%p
によって、ポインタを表示できる - スライス記法を使うと、コピーされずに同じ基底配列を指す。スライスの最初の要素ポインタがずれる
- ↓の例では中の要素がbyteなので、1バイト文だけずれる
s1 := []byte{16, 32, 48, 64, 80} s2 := s1[1:3] fmt.Printf("s1: %p %v len: %d cap: %d\n", s1, s1, len(s1), cap(s1)) fmt.Printf("s2: %p %v len: %d cap: %d\n", s2, s2, len(s2), cap(s2))
s1: 0xc0000120e0 [16 32 48 64 80] len: 5 cap: 5 s2: 0xc0000120e1 [32 48] len: 2 cap: 4
同じ基底配列を使っているのを確認する。
s1 := []byte{16, 32, 48, 64, 80} s2 := s1[1:3] s2 = append(s2, 100, 101) fmt.Printf("s1: %p %v len: %d cap: %d\n", s1, s1, len(s1), cap(s1)) fmt.Printf("s2: %p %v len: %d cap: %d\n", s2, s2, len(s2), cap(s2))
s1: 0xc0000120e0 [16 32 48 100 101] len: 5 cap: 5 s2: 0xc0000120e1 [32 48 100 101] len: 4 cap: 4
capを超えた場合、再割り当てされるので、s1, s2は同じ基底配列ではなくなる。
- s2の先頭要素のポインタが変わっている
- s2への変更がs1に反映されない
s1 := []byte{16, 32, 48, 64, 80} s2 := s1[1:3] s2 = append(s2, 100, 101, 102) fmt.Printf("s1: %p %v len: %d cap: %d\n", s1, s1, len(s1), cap(s1)) fmt.Printf("s2: %p %v len: %d cap: %d\n", s2, s2, len(s2), cap(s2))
s1: 0xc0000120e0 [16 32 48 64 80] len: 5 cap: 5 s2: 0xc0000120e8 [32 48 100 101 102] len: 5 cap: 8
スライス記法の3番目の引数で、新しいスライスの容量を制限できる。これによって、同じ基底配列に意図せず変更を加えるのを防げる。
s1 := []byte{16, 32, 48, 64, 80} s3 := s1[1:3:3] s3 = append(s3, 200, 201) fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1)) fmt.Printf("s3: %v, len: %d, cap: %d\n", s3, len(s3), cap(s3))
s1: [16 32 48 64 80], len: 5, cap: 5 s3: [32 48 200 201], len: 4, cap: 8
関連
なし。