英文:
Why is the method receiver not required to be a pointer when implementing sort.Interface for golang types?
问题
我正在阅读 sort 标准库包的文档,示例代码如下所示:
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
根据我的了解,对于会改变类型 T
的函数,需要使用 *T
作为其方法接收器。在 Len
、Swap
和 Less
的情况下,为什么它们能够工作?或者我是否误解了使用 T
和 *T
作为方法接收器之间的区别?
英文:
I am reading the docs for the sort stdlib package and the sample code reads like this:
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
As I've learnt, function that mutate a type T
needs to use *T
as its method receiver.
In the case of Len
, Swap
and Less
why does it work ? Or am I misunderstanding the difference between using T
vs *T
as method receivers ?
答案1
得分: 3
Go语言有三种引用类型:
- map(映射)
- slice(切片)
- channel(通道)
这些类型的每个实例在内部都持有指向实际数据的指针。这意味着当你传递这些类型的值时,该值会被复制,就像其他值一样,但内部指针仍然指向相同的值。
以下是一个快速示例(在playground上运行):
func dumpFirst(s []int) {
fmt.Printf("切片变量的地址:%p,第一个元素的地址:%p\n", &s, &s[0])
}
s1 := []int{1, 2, 3}
s2 := s1
dumpFirst(s1)
dumpFirst(s2)
输出结果类似于:
切片变量的地址:0x1052e110,第一个元素的地址:0x1052e100
切片变量的地址:0x1052e120,第一个元素的地址:0x1052e100
可以看到,切片变量的地址发生了变化,但切片中第一个元素的地址保持不变。
英文:
Go has three reference types:
- map
- slice
- channel
Every instance of these types holds a pointer to the actual data internally. This means that
when you pass a value of one of these types the value is copied like every other value but the
internal pointer still points to the same value.
Quick example (run on play):
func dumpFirst(s []int) {
fmt.Printf("address of slice var: %p, address of element: %p\n", &s, &s[0])
}
s1 := []int{1, 2, 3}
s2 := s1
dumpFirst(s1)
dumpFirst(s2)
will print something like:
address of slice var: 0x1052e110, address of element: 0x1052e100
address of slice var: 0x1052e120, address of element: 0x1052e100
You can see: the address of the slice variable changes but the address of the first element in that slice remains the same.
答案2
得分: 1
我刚刚对这个完全相同的问题有了一个小的顿悟。
正如已经解释过的那样,一个有类型的切片(不是切片的指针)可以实现sort.Interface
接口;部分原因是,尽管切片被复制了,但它的一个字段是一个指向数组的指针,所以对该后备数组的任何修改都将反映在原始切片中。
然而,通常情况下,这还不足以证明裸切片是一个可接受的接收器。通常不正确的是尝试将结构体作为方法的接收器进行修改,因为任何append()
调用都会改变切片的副本的长度,而不会修改原始切片的头部。切片的修改甚至可能触发新后备数组的初始化,完全断开了复制的接收器与原始切片的连接。
然而,由于排序的本质,这在sort.Sort
的情况下不是一个问题。它所执行的唯一修改数组的操作是Swap
,这意味着数组所需的内存将保持不变,因此切片的大小不会改变,因此切片的实际值(起始索引、长度和数组指针)也不会改变。
我相信这对很多人来说是显而易见的,但我刚刚意识到这一点,我觉得对其他想知道为什么sort
与裸切片相容的人可能有用。
英文:
I just had a minor epiphany regarding this exact same question.
As has already been explained, a typed slice (not a pointer to a slice) can implement the sort.Interface
interface; part of the reason for this is that, even though the slice is being copied, one of its fields is a pointer to an array, so any modification of that backing array will be reflected in the original slice.
Normally, though, this isn't enough of a justification for a bare slice to be an acceptable receiver. It's generally incorrect to try to modify a struct as a receiver of a method, because any append()
calls will change the slice copy's length without modifying the original slice's headers. Slice modification may even trigger the initialization of a new backing array, completely disconnecting the copied receiver from the original slice.
By the very nature of sorting, however, this isn't a problem in the sort.Sort
case. The only array-modifying operation it exercises is Swap
, meaning the array's required memory will remain the same, so the slice will not change size, and therefore there will be no changes to the slice's actual values (starting index, length, and array pointer).
I'm sure this was obvious to a lot of people, but it just dawned on me, and I thought it might be useful to others wondering why sort
plays nicely with bare slices.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论