How do you clear a slice in Go?
Setting the slice to nil
is the best way to clear a slice. nil
slices in go are perfectly well behaved and setting the slice to nil
will release the underlying memory to the garbage collector.
See playground
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)
}
Prints
letters = [a b c d]
4
4
0 a
1 b
2 c
3 d
letters = []
0
0
letters = [e]
1
1
0 e
Note that slices can easily be aliased so that two slices point to the same underlying memory. The setting to nil
will remove that aliasing.
This method changes the capacity to zero though.
It all depends on what is your definition of 'clear'. One of the valid ones certainly is:
slice = slice[:0]
But there's a catch. If slice elements are of type T:
var slice []T
then enforcing len(slice)
to be zero, by the above "trick", doesn't make any element of
slice[:cap(slice)]
eligible for garbage collection. This might be the optimal approach in some scenarios. But it might also be a cause of "memory leaks" - memory not used, but potentially reachable (after re-slicing of 'slice') and thus not garbage "collectable".
I was looking into this issue a bit for my own purposes; I had a slice of structs (including some pointers) and I wanted to make sure I got it right; ended up on this thread, and wanted to share my results.
To practice, I did a little go playground: https://play.golang.org/p/9i4gPx3lnY
which evals to this:
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)
}
Running that code as-is will show the same memory address for both "meow" and "meow2" variables as being the same:
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan
which I think confirms that the struct is garbage collected. Oddly enough, uncommenting the commented print line, will yield different memory addresses for the meows:
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan
I think this may be due to the print being deferred in some way (?), but interesting illustration of some memory mgmt behavior, and one more vote for:
[]MyStruct = nil