Before Go 1.17 it was not possible to get a reference to the backing array of a slice without resorting to the reflect
and unsafe
packages to inspect the raw slice header. Not ideal.
Some resources refer to "backing array" as the "underlying array". These are the same thing.
However, since 1.17 it is possible to convert a slice to a pointer to an array using a type conversion:
a1 := [3]string{"👁", "👃", "👁"}
s := a1[:]
a2 := (*[3]string)(s)
In the example above, a2
will be equal to the memory address of a1
. In other words, &a1 == a2
is true.
If we diagram the situation it looks like this:
There are some limits you should be aware of.
Limit 1: Length of slice
The length of the array can not be greater than the length of the slice, even if the backing array has more capacity.
a1 := [3]string{"👁", "👃", "👁"}
s := a1[:2] // get a slice of len 2
a2 := (*[3]string)(s) // will panic!
The above example will panic. We can’t convert a slice of length 2 to a pointer to an array of length 3.
Limit 2: Begins at slice offset
The pointer to the array will always start at the beginning of the slice. If your slice does not start at the beginning of its backing array your array pointer will not do so either.
a1 := [3]string{"👁", "👃", "👁"}
s := a1[1:] // get a slice of len 2 with offset 1
a2 := (*[2]string)(s)
Since slice s
has a length of 2 we can only convert to a *[2]string
.
We will not be able to get a reference to the a1[0]
element. The diagram below illustrates this.
Reference to the full backing array
Put together, these limits mean that you can only get an array that references the “window” of any specific slice.
If you want a reference to the full backing array of a slice you will need to know how it was created:
- Created via
make
, a literal or returned fromappend
: The slice will always start at the first element, so you can re-slice up to its capacity and get a reference to the entire backing array. - Created via slicing of an array: You will already have the full array reference.
- Created via re-slicing: Find out how the original slice was created, if it was also re-sliced, follow the originals until you find a non re-sliced slice.
Copy slice to array (1.20 and later)
Since Go 1.20 you also have the option to directly convert slices to new arrays, no pointers required. The limits above regarding the “window” still apply.
a1 := [3]string{"👁", "👃", "👁"}
s := a1[:]
a2 := [3]string(s)
In the above example a2
will be a new array containing the same values as a1
.
Run the code below to see the differences between the two conversions.
package main
import (
"fmt"
)
func main() {
original := [3]string{"👁", "👃", "👁"}
s := original[:]
ptr := (*[3]string)(s)
cpy := [3]string(s)
fmt.Printf("&original: %p\n", &original)
fmt.Printf("ptr: %p\n", ptr)
fmt.Printf("&cpy: %p\n", &cpy)
fmt.Printf("ptr == &original: %v\n", ptr == &original)
fmt.Printf("&cpy == &original: %v\n", &cpy == &original)
}
Get my free newsletter every second week
Used by 500+ developers to boost their Go skills.
"I'll share tips, interesting links and new content. You'll also get a brief guide to time for developers for free."