Search code examples
goappendslicecapacity

Slices in Go: why does it allow appending more than the capacity allows?


The capacity parameter in making a slice in Go does not make much sense to me. For example,

aSlice := make([]int, 2, 2) //a new slice with length and cap both set to 2
aSlice = append(aSlice, 1, 2, 3, 4, 5) //append integers 1 through 5
fmt.Println("aSlice is: ", aSlice)  //output [0, 0, 1, 2, 3, 4, 5]

If the slice allows inserting more elements than the capacity allows, why do we need to set it in the make() function?


Solution

  • The builtin append() function uses the specified slice to append elements to if it has a big enough capacity to accomodate the specified elements.

    But if the passed slice is not big enough, it allocates a new, big enough slice, copies the elements from the passed slice to the new slice and append the elements to that new slice. And returns this new slice. Quoting from the append() documentation:

    The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

    When making a slice with make if the length and capacity are the same, the capacity can be omitted, in which case it is defaulted to the specified length:

    // These 2 declarations are equivalent:
    s := make([]int, 2, 2)
    s := make([]int, 2)
    

    Also note that append() appends elements after the last element of the slice. And the above slices already have len(s) == 2 right after declaration so if you append even just 1 element to it, it will cause a reallocation as seen in this example:

    s := make([]int, 2, 2)
    fmt.Println(s, len(s), cap(s))
    s = append(s, 1)
    fmt.Println(s, len(s), cap(s))
    

    Output:

    [0 0] 2 2
    [0 0 1] 3 4
    

    So in your example what you should do is something like this:

    s := make([]int, 0, 10) // Create a slice with length=0 and capacity=10
    fmt.Println(s, len(s), cap(s))
    s = append(s, 1)
    fmt.Println(s, len(s), cap(s))
    

    Output:

    [] 0 10
    [1] 1 10
    

    I recommend the following blog articles if you want to understand slices in more details:

    Go Slices: usage and internals

    Arrays, slices (and strings): The mechanics of 'append'