Go Slices

A Go Slice contains three elements: data, length, and capacity.

s := make([]int, 0, 10)

The variable s is a slice of ints with a length of 0 and a capacity of 10. The built-in len() and cap() functions allow you to get the length and capacity of a slice:

len(s) == 0
cap(s) == 10

To increase the length of a slice, simply re-slice:

s = s[0:5]
// len(s) == 5
// cap(s) == 10

To decrease the length, you can take a sub-slice:

s = s[0:1]
// len(s) == 1

There are some shorter ways to invoke make():

a := make([]int, 10) 
// len(a) == cap(a) == 10

b := make([]int)
// len(b) == cap(b) == 0

That’s all well and good, but what if you need to increase the length of a slice beyond its capacity? To do that, you need to allocate a new slice and copy the contents of the old slice to the new one. (The function “copy” is another built-in.)

t := make([]int, len(s), 20)
copy(t, s)

The Effective Go document takes this example a bit further, implementing an Append function that appends one slice to another, resizing it if necessary.

Slices are backed by arrays; when you make() a slice of a specific capacity, an array of that capacity is allocated in the background. The slice effectively becomes a “smart pointer” to that array. If you pass that slice (or a subslice of that slice) to another function, it is passed as a pointer to that same array. This makes sub-slices very cheap to create – it’s the allocation of the backing array that is expensive.

The Go standard library includes a number of container packagesvector for instance – that eliminate the need to manually manage slices. Use slices for speed, and more elaborate container classes for convenience. (Saying that, I still use slices for most things.)

You may be wondering why you need to go to all this trouble. After all, a lot of languages provide dynamically resized arrays as primitives. The reason for this is tied to Go’s philosophy. The language designers don’t presume to know what the appropriate allocation policy is for your program; instead they give you the tools you need to build your own data structures.

28440 views and 4 responses

  • Aug 20 2010, 3:16 PM
    Yves Junqueira responded:
    Coming from Python, I'm still very annoyed with Go slices, arrays and vectors.

    - arrays are too limited.
    - slices are too annoying to work with. Simple operations like adding an item takes two lines of code (need grow length first), even after assuming you won't go over the slice's cap.
    - vectors are not idiomatic and using them requires a lot of typing. Also, creating vectors of anything other than Int and String is very annoying.

    I wanted an idiomatic way to do this:

    // Append item to the end of slice.
    // caller is responsible for checking if slice cap is big enough.
    func Append(slice []interface{}, item interface{}) (newSlice []interface{}) {
    l := len(slice)
    newSlice = slice[0:l+1]
    newSlice[l+1] = item
    return newSlice
    }

    Not that this example would work. I couldn't find a way to satisfy []interface{} for the first argument. But I hope you get the idea.

    So in summary I like the "cap" of slices, which is a plus compared to Python's list. But I don't like that you still have to manually manage the array within it.

  • Aug 20 2010, 3:30 PM
    Yves Junqueira responded:
    Also, the re-slicing is very verbose. Worst-case real example:

    DhtStats.engines = DhtStats.engines[0:len(DhtStats.engines)+1]
    DhtStats.engines[len(DhtStats.engines)] = v

    Of course I could create an alias for the slice (e = DhtStats.engines), but then I'll need 3 lines instead :-).

  • Aug 20 2010, 5:33 PM
    Andrew Gerrand responded:
    This is the price you pay for control over allocation. It's substantially easier than doing it in C, but you get close to the same level of control. I agree that things can get a little cumbersome on occasion, but I've not found it particularly annoying.

    In your second example, I would just define a method "AddEngine" on whatever type DhtStats is. It's more lines of code, but allows you to encapsulate that bit of syntactical complexity. It's a compromise, though.

  • Jun 20 2012, 12:14 AM
    Bob Holness responded:
    You can append to a slice now using the 'append' built-in:

    slice = append(slice, newItem)