Goでスライスをクリアする適切な方法は何ですか?
go forums で見つけたものは次のとおりです。
// test.go
package main
import (
"fmt"
)
func main() {
letters := []string{"a", "b", "c", "d"}
fmt.Println(cap(letters))
fmt.Println(len(letters))
// clear the slice
letters = letters[:0]
fmt.Println(cap(letters))
fmt.Println(len(letters))
}
これは正しいです?
明確にするために、バッファーは再利用できるようにクリアされます。
例は、 Buffer.Truncate bytesパッケージの関数です。
ResetはTruncate(0)を呼び出すだけです。したがって、この場合、行70は次のように評価されます。b.buf = b.buf [0:0]
http://golang.org/src/pkg/bytes/buffer.go
// Truncate discards all but the first n unread bytes from the buffer.
60 // It panics if n is negative or greater than the length of the buffer.
61 func (b *Buffer) Truncate(n int) {
62 b.lastRead = opInvalid
63 switch {
64 case n < 0 || n > b.Len():
65 panic("bytes.Buffer: truncation out of range")
66 case n == 0:
67 // Reuse buffer space.
68 b.off = 0
69 }
70 b.buf = b.buf[0 : b.off+n]
71 }
72
73 // Reset resets the buffer so it has no content.
74 // b.Reset() is the same as b.Truncate(0).
75 func (b *Buffer) Reset() { b.Truncate(0) }
それはすべて、「クリア」の定義に依存します。有効なものの1つは確かに:
slice = slice[:0]
しかし、キャッチがあります。スライス要素がタイプTの場合:
var slice []T
len(slice)
を強制的にゼロにする、上記の「トリック」により、does n'tの要素を作成する
slice[:cap(slice)]
ガベージコレクションの対象。これは、いくつかのシナリオでは最適なアプローチかもしれません。しかし、それは「メモリリーク」の原因にもなる可能性があります。メモリは使用されませんが、潜在的に到達可能(「スライス」の再スライス後)で、ガベージ「収集可能」ではありません。
スライスをnil
に設定することが、スライスをクリアする最良の方法です。実行中のnil
スライスは完全に適切に動作し、スライスをnil
に設定すると、基になるメモリがガベージコレクターに解放されます。
package main
import (
"fmt"
)
func dump(letters []string) {
fmt.Println("letters = ", letters)
fmt.Println(cap(letters))
fmt.Println(len(letters))
for i := range letters {
fmt.Println(i, letters[i])
}
}
func main() {
letters := []string{"a", "b", "c", "d"}
dump(letters)
// clear the slice
letters = nil
dump(letters)
// add stuff back to it
letters = append(letters, "e")
dump(letters)
}
プリント
letters = [a b c d]
4
4
0 a
1 b
2 c
3 d
letters = []
0
0
letters = [e]
1
1
0 e
2つのスライスが同じ基礎メモリを指すように、スライスは簡単にエイリアスできることに注意してください。 nil
に設定すると、そのエイリアスが削除されます。
ただし、この方法では容量がゼロに変更されます。
私は自分の目的のためにこの問題を少し調べていました。構造体のスライス(いくつかのポインターを含む)があり、それが正しいことを確認したかった。このスレッドで終わった、と私の結果を共有したかった。
練習するために、少し遊び場を作りました: https://play.golang.org/p/9i4gPx3lnY
これに評価する:
package main
import "fmt"
type Blah struct {
babyKitten int
kittenSays *string
}
func main() {
meow := "meow"
Blahs := []Blah{}
fmt.Printf("Blahs: %v\n", Blahs)
Blahs = append(Blahs, Blah{1, &meow})
fmt.Printf("Blahs: %v\n", Blahs)
Blahs = append(Blahs, Blah{2, &meow})
fmt.Printf("Blahs: %v\n", Blahs)
//fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
Blahs = nil
meow2 := "nyan"
fmt.Printf("Blahs: %v\n", Blahs)
Blahs = append(Blahs, Blah{1, &meow2})
fmt.Printf("Blahs: %v\n", Blahs)
fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
}
そのコードをそのまま実行すると、「meow」変数と「meow2」変数の両方に同じメモリアドレスが表示されます。
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan
これは、構造体がガベージコレクションされていることを確認していると思います。奇妙なことに、コメント付きの印刷行のコメントを外すと、ニャーごとに異なるメモリアドレスが生成されます。
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan
これは、何らかの方法で印刷が延期された(?)が原因であると思われますが、メモリ管理の動作の興味深い例と、もう1つ投票します。
[]MyStruct = nil