How to keep a strong reference in Go?

huangapple go评论82阅读模式
英文:

How to keep a strong reference in Go?

问题

在Go语言中,可以通过使用指针来保持对切片的强引用。在给定的代码中,可以将slicesArray声明为[]*[]int,这样就可以保持对切片的强引用。这样,在将slice设置为nil后,仍然可以通过*(slicesArray[0])来访问切片的值。

关于切片引用和声明[][]int的内存使用方面的差异,可以说在这种情况下,两者的内存使用是相同的。因为切片本身就是对底层数组的引用,而不是实际存储数据的地方。因此,无论是使用切片引用还是声明[][]int,都不会有明显的内存使用优势。

关于这个问题的官方文档中可能没有明确说明,但可以参考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?

答案1

得分: 6

TL;DR: 你只需要复制切片的值。切片类型的值是对一个底层数组的描述符:

slice2 := slice

现在,slice2 将引用相同的共享底层数组,因此在 slice 被置零后仍然可以访问到它。

详细解释:

slice := make([]int, 5)

这创建了一个名为 slice 的局部变量,类型为 []int(并且将通过 make 在后台创建的大小为 5 的数组的描述符进行初始化)。它是一个切片,是对自动在后台创建的底层数组的连续部分的描述符。

slicesArray[0] = &slice

这将局部变量 slice 的地址存储到 slicesArray 的第一个元素中。注意,slicesArray 只是一个指针的切片(这些指针可能指向类型为 []int 的值,但现在并不重要)。

所以,原始的 slice 还没有被复制。

因此,当你将类型为 []int 的唯一值(即名为 slice 的局部变量)置零时,你也将唯一的切片值置零(并且将失去对其支持数组的唯一引用)。

你想在 slice 被置零后保留切片值吗?只需复制它(切片值)。复制切片类型的值只会复制描述符,支持数组不会被复制;复制后的切片将引用相同的支持数组(它是共享的):

slice2 := slice // 复制切片值
slice = nil

在这之后,*(slicesArray[0]) 仍然指向一个值为 nil 的切片,但我们有原始切片的副本(和共享的支持数组)。

因此,执行以下操作:

slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])

将再次打印出 25Go Playground

你应该保留切片值还是切片的指针?

由于切片只是相对较小的描述符,所以应该保留并使用切片值。切片值已经包含对支持数组的“引用”。通过使用指向切片的指针添加另一个间接引用只会增加复杂性并稍微降低速度。与 Go 中的“真实”数组相比,切片已经被设计为小巧、高效和灵活的。

当然,在某些情况下,指向切片值的指针也可能有用,例如,如果你想创建一个类似于内置的 append() 函数但不返回新切片的函数。或者当你创建一个自定义类型,其底层类型是切片类型,并且为该类型定义修改切片值的方法时(在这种情况下,需要使用指针接收器)。

进一步阅读:(详细解释一切)

Go 切片:用法和内部原理

数组、切片(和字符串):'append' 的机制

英文:

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'

huangapple
  • 本文由 发表于 2015年6月17日 03:13:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/30876241.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定