alediaferia
alediaferia

Reputation: 2617

How to keep a strong reference in Go?

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

Answers (1)

icza
icza

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

Related Questions