英文:
Pointer and slice reference type - receiver
问题
我认为一旦有人提醒我,这个问题就会变得很明显,但现在我还没有理解以下内容。
type Stack []interface{}
func (stack *Stack) Push(x interface{}) {
*stack = append(*stack, x)
}
我有一个名为Stack的类型,它是一个空接口的切片。鉴于它是空的,Push方法满足了该接口。鉴于切片是引用类型,为什么不能通过值传递'stack'接收器呢?此外,在上面的示例中,接收器作为指针传递,为什么append内置函数需要再次传递指针?
也就是说,鉴于切片是指向底层数组的引用指针,为什么这个方法不起作用呢?
func (stack Stack) Push(x interface{}) {
stack = append(stack, x)
}
英文:
I think this will be a bit obvious once it's called out to me, but right now the following isn't clicking.
type Stack []interface{}
func (stack *Stack) Push(x interface{}) {
*stack = append(*stack, x)
}
I have a type called Stack that is a slice of empty interfaces. Given that it's empty, the Push method satisfies the interface. Given that a slice is a reference type, why can't the 'stack' receiver just be passed in by value? Furthermore, in the example above the receiver is passed as a pointer why does the append built-in need to be passed by pointer again?
IE why wouldn't this work, given that the slice is a reference pointer to an underlying array?
func (stack Stack) Push(x interface{}) {
stack = append(stack, x)
}
答案1
得分: 8
请看这篇Go博客上的文章。它详细解释了正在发生的事情,并完整回答了你的问题。
从将切片传递给函数这一节中:
需要明白的是,尽管切片包含一个指针,但它本身是一个值。在底层,它是一个包含指针和长度的结构值。它不是指向结构的指针。
因此,如果你想要使用append
修改切片,你要么需要一个指针接收器,要么需要将切片作为值返回。
如果你只想修改切片的内容,你可以简单地按值传递切片:
尽管切片头部是按值传递的,但头部包含一个指向数组元素的指针,因此原始切片头部和传递给函数的头部副本都描述了同一个数组。因此,当函数返回时,可以通过原始切片变量看到修改后的元素。
使用append
时,你正在修改切片的头部。并且
因此,如果我们想要编写一个修改头部的函数,我们必须将其作为结果参数返回。
或者:
修改切片头部的另一种方法是将指针传递给它。
你似乎对指针的使用有些困惑。请参阅规范:
对于类型为T的操作数x,地址操作符
&x
生成一个类型为*T的指针,指向x。
并且:
对于指针类型*T的操作数x,指针间接操作符
*x
表示指向x所指向的类型为T的变量。
因此,你的示例*stack = append(*stack, x)
并不意味着你正在将指针传递给append
,相反,你正在解引用指针以传递它所指向的值。
英文:
See this article on the Go blog. It explains in detail what is happening and fully answers your question.
From the section Passing slices to functions:
> It's important to understand that even though a slice contains a pointer, it is itself a value. Under the covers, it is a struct value holding a pointer and a length. It is not a pointer to a struct.
As a result you either need a pointer receiver or you need to return the slice as a value if you want to modify it with append
.
If you just want to modify the contents of a slice you can simply pass the slice by value:
> Even though the slice header is passed by value, the header includes a
> pointer to elements of an array, so both the original slice header and
> the copy of the header passed to the function describe the same array.
> Therefore, when the function returns, the modified elements can be
> seen through the original slice variable.
With append
you are modifying the slice header. And
> Thus if we want to write a function that modifies the header, we must
> return it as a result parameter
Or:
> Another way to have a function modify the slice header is to pass a pointer to it.
You also seem to have a confusion on the use of pointers. See the spec:
> For an operand x of type T, the address operation &x generates a pointer of type *T to x.
And:
> For an operand x of pointer type *T, the pointer indirection *x denotes the variable of type T pointed to by x.
Thus your example *stack = append(*stack, x)
doesn't mean that you are passing a pointer to append
, quite the opposite - you are dereferencing the pointer to pass the value it's pointing to.
答案2
得分: 2
你遇到的问题是append
函数返回一个新切片的引用。除非函数的接收者是指针类型,否则无法更改其值。
我建议你创建一个包装结构体:
type Stack struct{
data []interface{}
size int
}
func (s *Stack) Push(i interface{}){
s.data = append(s.data,i)
s.size++
}
英文:
The problem you are having is that append
returns a reference to the new slice. You cannot change the value of the function's receiver unless it is a pointer.
What I would recommend rather is making a wrapper struct:
type Stack struct{
data []interface{}
size int
}
func (s *Stack) Push(i interface{}){
s.data = append(s.data,i)
s.size++
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论