Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer and slice reference type - receiver

Tags:

go

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)
}
like image 739
Patrick Wolf Avatar asked Apr 24 '15 03:04

Patrick Wolf


People also ask

What is a pointer receiver?

Pointer receiver passes the address of a type to the function. The function stack has a reference to the original object. So any modifications on the passed object will modify the original object. Let's understand this with the example- package mainimport ( "fmt"

Is slice a pointer?

Slices are pointers to arrays, with the length of the segment, and its capacity. They behave as pointers, and assigning their value to another slice, will assign the memory address.

Is Golang slice a reference type?

Go slice is reference type A slice is a reference type in Go. This means that when we assign a reference to a new variable or pass a slice to a function, the reference to the slice is copied. In the code example, we define a slice and assign the slice to a new variable.

Is slice passed by reference?

When we pass a slice to a function as an argument the values of the slice are passed by reference (since we pass a copy of the pointer), but all the metadata describing the slice itself are just copies.


1 Answers

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.

like image 95
IamNaN Avatar answered Sep 20 '22 10:09

IamNaN