How do you clear a slice in Go?

ArraysGoSlice

Arrays Problem Overview


What is the appropriate way to clear a slice in Go?

Here's what I've found in the 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))
}

Is this correct?

To clarify, the buffer is cleared so it can be reused.

An example is Buffer.Truncate function in the bytes package.

Notice that Reset just calls Truncate(0). So it appears that in this case line 70 would evaluate: 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) }

Arrays Solutions


Solution 1 - Arrays

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.

Solution 2 - Arrays

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".

Solution 3 - Arrays

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChris WeberView Question on Stackoverflow
Solution 1 - ArraysNick Craig-WoodView Answer on Stackoverflow
Solution 2 - ArrayszzzzView Answer on Stackoverflow
Solution 3 - Arraysmax garveyView Answer on Stackoverflow