Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the underlying array of a slice in Go?

Tags:

Let's say I have the following array of ints of length 3:

nums := [3]int{1,2,3}

Then I grab the slice of just the first two items

numSlice := nums[:2]

Invoking cap on numSlice and nums yields 3 in both cases, and len yields 2 and 3 respectively.

If I then append to that slice (numSlice = append(numSlice, 10)), the underlying array (nums) is now [1 2 10]. cap remains at 3 for both, as the underlying array of the slice is the same, and len for the slice is now 3.

However, if I append to that slice again (numSlice = append(numSlice, 20)), the underlying array of the slice must change - we see this is the case when cap now has doubled for numSlice and len is now 4.

Sorry for the overwrought explanation, just walking myself through it, but can someone explain to me what happens under the hood to the underlying array and how to get the reference to the new array?

like image 683
Jimmy Gong Avatar asked Apr 19 '16 01:04

Jimmy Gong


People also ask

How do you slice an array in Go?

Like arrays, slices are also used to store multiple values of the same type in a single variable. However, unlike arrays, the length of a slice can grow and shrink as you see fit. In Go, there are several ways to create a slice: Using the []datatype{values} format.

How do I find my slice element?

In the Go slice, you can search an element of string type in the given slice of strings with the help of SearchStrings() function. This function searches for the given element in a sorted slice of strings and returns the index of that element if present in the given slice.

What is difference between array and slice in Go?

Slices in Go and Golang The basic difference between a slice and an array is that a slice is a reference to a contiguous segment of an array. Unlike an array, which is a value-type, slice is a reference type. A slice can be a complete array or a part of an array, indicated by the start and end index.


2 Answers

First, if you haven't already, you should read this official blog post about slice internals. That should clear up everything.

Now to access the underlying array, you can use a combination of reflect and unsafe. In particular, reflect.SliceHeader contains a Data field which contains a pointer to the underlying array of a slice.

Example adapted from the documentation of the unsafe package:

s := []int{1, 2, 3, 4}
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
data := *(*[4]int)(unsafe.Pointer(hdr.Data))
like image 177
Simon Avatar answered Sep 20 '22 17:09

Simon


Just as a headsup, answering your second question. Starting from Go 1.17, you can do it like this

(*[2]int)(numSlice)

Playground

package main

import (
    "fmt"
)

func main() {
    nums := [3]int{1, 2, 3}
    numSlice := nums[:2]
    underArr1 := (*[2]int)(numSlice)
    fmt.Println(&underArr1[0]) //0xc000016018

    numSlice = append(numSlice, 10)
    underArr2 := (*[3]int)(numSlice)
    fmt.Println(&underArr2[0]) //0xc000016018 - same
    fmt.Println(nums) // [1 2 10]

    numSlice = append(numSlice, 20)
    underArr3 := (*[3]int)(numSlice)
    fmt.Println(&underArr3[0]) //0xc000078030 - different
    fmt.Println(cap(numSlice)) // 6
}

To be honest, you don't have to convert to array pointer to see the adresses, I just do it to answer your second question.

The behaviour is indeed the way you described it. When you append 10, you still have one field left in your underlying array (because its length is 3, but your numSlice is 2), and even though it is currently occupied by a 3, it can be used, and 3 is overwritten by 10.

When you append a 20, there are no fields left, so it creates a new underlying array (most likely 6 fields long, twice as big) and copies all the data from the original array there and moves the pointer to that array.

like image 45
Highstaker Avatar answered Sep 22 '22 17:09

Highstaker