Reputation: 2617
Is there any way I can keep a strong reference in Go?
Given the following convoluted piece of code:
package main
import (
"fmt"
)
func main() {
slice := make([]int, 5)
slice[3] = 25 // whatever index between 0 and 4 included I don't care
slicesArray := make([]*[]int, 2)
slicesArray[0] = &slice
fmt.Println((*(slicesArray[0]))[3])
slice = nil
fmt.Println((*(slicesArray[0]))[3])
}
This program of course crashes because once setting the slice to nil
the garbage collector marks the memory area as dirty.
But is there a way to tell Go that my slice of pointers to slices should keep a strong reference to those slices?
Also, is there any memory usage gain in keeping references to slices rather than declaring slicesArray as [][]int
? Is there any doc clearly stating how this is supposed to work?
Upvotes: 4
Views: 425
Reputation: 418575
TL;DR: (summary)
You just need to make a copy of the slice value. A value of slice type is a descriptor to an underlying array:
slice2 := slice
Now slice2
will refer to the same, shared underlying array and so it will be reachable after slice
is zerored.
In long:
slice := make([]int, 5)
This creates a local variable named slice
of type []int
(and will be initialized with a descriptor referring to an array of size 5
created in the background by make
). It's a slice which is a descriptor to a contiguous part of an underlying array which is created automatically in the background.
slicesArray[0] = &slice
This stores the address of the local variable (named slice
) into the 0th element of slicesArray
. Note that slicesArray
is just a slice of pointers (which pointers may point to values of type []int
but that doesn't matter now).
So still no copy of the original slice
is created.
And so when you zero the only value of type []int
(which is the local variable named slice
), you zero the only slice value (and you'll lose the only reference to its backing array).
You want to keep the slice value after slice
has been zeroed? Just make a copy of it (of the slice value). Copying a value of slice type only makes a copy of the descriptor, the backing array is not copied; and the copy will refer to the same backing array (it's shared):
slice2 := slice // makes a copy of the slice value
slice = nil
After this *(slicesArray[0])
will still point to a slice value being nil
, but we have a copy of the original slice (and the shared backing array).
So doing:
slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])
Will print again 25
. Go Playground
Should you keep slice values or pointers to slices?
Since slices are just descriptors which are relatively small, you should keep and work with slice values. A slice value already contains a "reference" to a backing array. Adding another indirection by using a pointer to a slice just complicates and slightly slows things down. A slice is already designed to be small, efficient and flexible (compared to "real" arrays in Go).
Of course a pointer to a slice value may also be useful in some cases, for example if you would ever want to create a function similar to the builtin append()
function without returning the new slice. Or when you create a custom type whose underlying type is a slice type, and you define methods for this type which modify the slice value (in this case a pointer receiver is necessary).
Further readings: (the docs explaining everything in detail)
Go Slices: usage and internals
Arrays, slices (and strings): The mechanics of 'append'
Upvotes: 6