Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the capacity of a slice change when you drop the first n items but not the last n items?

Tags:

go

I am going through the tour of Go and wanted to know the following:

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Result:

len=6 cap=6 [2 3 5 7 11 13]
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

Why does the capacity of the slice stay the same when you drop the last 2 items but changes when you drop the first 2?

https://play.golang.org/p/ZNKwOYKDqOi

like image 779
user_78361084 Avatar asked Mar 20 '19 00:03

user_78361084


People also ask

What is the use of capacity in slice in Golang?

In Go, the length of a slice tells you how many elements it contains. It can be obtained using the len() function. The capacity is the size of the slice's underlying array and can be obtained with the cap() function.

How do you remove an element from a slice?

Removing an Element from a Slice Items need to be removed from a slice by slicing them out. To remove an element, you must slice out the items before that element, slice out the items after that element, then append these two new slices together without the element that you wanted to remove.

How do you remove the first element of a slice in Golang?

Remove first element of slice (shift, pop(0))

Is Golang slice inclusive?

operator, Go calculates the size of the array for us. We create a slice from the vals array. The resulting slice contains elements starting from index 5 up to index 7; the upper bound is non-inclusive.


2 Answers

Go slices are implemented as a struct:

src/runtime/slice.go:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

Revise your printSlice function to show the pointer to the underlying array:

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    var ptr *int
    if cap(s) >= 1 {
        ptr = &s[:cap(s)][0]
    }
    fmt.Printf("ptr=%p len=%d cap=%d %v\n", ptr, len(s), cap(s), s)
}

Playground: https://play.golang.org/p/pk3cpE_LsUV

Output:

ptr=0x450000 len=6 cap=6 [2 3 5 7 11 13]
ptr=0x450000 len=4 cap=6 [2 3 5 7]
ptr=0x450008 len=2 cap=4 [5 7]

See how your slicing operations adjust the pointer, the length, and the capacity. A slice is simply a view of or window into the underlying array.


References:

The Go Blog: Go Slices: usage and internals

The Go Programming Language Specification:

  • Slice types

  • Slice expressions

like image 153
peterSO Avatar answered Oct 24 '22 22:10

peterSO


A slice has a buffer under the covers. The capacity is the size of that buffer. Removing items from the end does not alter the buffer size.

You are creating a new slice from an existing slice but starting the index of the new slice at 2. The new slice still points to the same underlying buffer (but offset by 2). The buffer size (and capacity) for this new slice is thus 2 less.

Capacity makes sense when you see how append works. Append tries to reuse the same underlying buffer - but if the capacity is filled, then a reallocation and copy is performed.

like image 26
colm.anseo Avatar answered Oct 24 '22 22:10

colm.anseo