指针和切片引用类型 – 接收器

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

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++
}

huangapple
  • 本文由 发表于 2015年4月24日 11:32:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/29838240.html
匿名

发表评论

匿名网友

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

确定