I'm currently teaching myself Go, and I'm having trouble understanding a certain behavior:
package main
import (
"fmt"
)
type List struct {
n int
}
func (l List) Increment() {
l.n += 1
l.LogState() // size: 1
}
func (l List) LogState() {
fmt.Printf("size: %v\n", l.n)
}
func main() {
list := List{}
list.Increment()
fmt.Println("----")
list.LogState() // size: 0
}
https://play.golang.org/p/-O24DiNPkxx
LogState is executed twice. The initial time, during the Increment call, it prints size: 1 but after Increment has returned it prints size: 0. Why are those values different?
The reason your nodes are not added to the original linkedList because you are not using pointer to the struct. So even if the Increment function in your example code changes the value. The copy of the struct is changed not the actual struct.
You can declare methods with pointer receivers. This means the
receiver type has the literal syntax *T for some type T. (Also, T
cannot itself be a pointer such as *int.)
If you want to change the linkedlistNode struct counter to show the nodes added to the list you should be using a pointer type receiver on both methdos working to modify the linked list as:
func (l *LinkedList) AddInitialValue(v interface{})
func (l *LinkedList) LogState()
And Inside the main pass an address to the linkedList to use those pointer type receivers as:
func main() {
list := &LinkedList{}
list.AddInitialValue(9)
fmt.Println("----")
list.LogState() // size: 0
}
Working Code Go playground
Note:-
There are two reasons to use a pointer receiver.
To modify the value that its receiver points to.
To avoid copying the value on each method call. This can be more efficient if the receiver is a large struct
For more information go through Method Sets
With Increment and LogState defined the way you've defined them, you are working only with the copy of the value of List. This means that if you make some changes inside Increment function, they are visible only inside Increment's function scope and only for the remainder of that particular scope's existence. To confirm you are always working with a copy of of the initial List value, you can log &list before executing Increment function and &l inside the same function.
If you want to make changes permanent, you should work with a pointer to a memory address. That means your your function should be defined like this:
func (l *List) Increment()
func (l *List) LogState()
This way, you are passing a memory reference (pointer to an address in memory) and every time you change a value of l, you are changing it on the passed memory reference and it reflects everywhere.
Related
I have some code which is supposed to increment a count.
Here is the struct containing the count variable:
type PipelineData struct {
nodeData map[string]map[string]int
lastBurstResults map[string]map[string]string
burstReady map[string]bool
lastExecutionTime map[string]time.Time
currentNodeSize uint64
}
As you can see there is a member entitled currentNodeSize. This variable is intended to increment ever time the function addNodeData is called. You can see that the function addNodeData calls the function addCount which then uses an atomic incrementer.
func (p PipelineData) addNodeData(key string) {
nodeSlot := clusterScenario.GetNodeSlotByHashSlot(key)
i:=p.nodeData[nodeSlot][key]
i++
p.nodeData[nodeSlot][key]=i
p.addCount()
fmt.Println("Adding node count ",p.currentNodeSize)
}
func (p PipelineData) addCount(){
atomic.AddUint64(&p.currentNodeSize, 1)
}
Unfortunately, when I run this:
p.addNodeData("{pipelinetest}.key");
p.addNodeData("{pipelinetest}.key");
p.addNodeData("{pipelinetest}.key");
p.addNodeData("{pipelinetest}.key");
p.addNodeData("{pipelinetest}.key");
p.addNodeData("{pipelinetest}.key");
Dump(p.currentNodeSize)
The output is 0.
Here is the initialization of the PipelineData struct:
p = &PipelineData{
nodeData:make(map[string]map[string]int,0),
lastBurstResults:make(map[string]map[string]string,0),
burstReady:make(map[string]bool,0),
lastExecutionTime:make(map[string]time.Time,0),
currentNodeSize:0,
}
for i,_ := range clusterScenario.masterNodes{
p.nodeData[i]=make(map[string]int,0)
}
I understand the community has asked me to do my research. I'm not sure what to do at the point. Any help would be appreciated.
I also tried a general incrementor using a variable and that produced the same result which is why I tried an atomic incrementor
addCount takes a value receiver, not a pointer, so it's operating on a copy of the struct, incrementing the field of the copy and then discarding the copy. Change it instead to take a pointer:
func (p *PipelineData) addCount(){
This should resolve your issue.
I've started learning Go these days and got stuck in trying to pass a struct property's value (a slice) to a function. Apparently it's being passed as a reference (or it holds a pointer to its slice) and changes made inside the function affect it.
Here is my code, in which testFunction is supposed to receive a slice, remove its first 3 elements and print the updated values, but without affecting it externally:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
testFunction(test.testArray)
fmt.Println(test.testArray)
}
func testFunction(array []float64) {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
That outputs:
[10 20 30 40 50]
[40 50]
[40 50 50 50 50]
My question is: what is causing the third fmt.Println to print this strange result?
Playground: https://play.golang.org/p/G8W3H085In
p.s.: This code is only an example. It's not my goal to remove the first elements of something. I just wanna know what is causing this strange behaviour.
Usually we don't know whether a given call to append will cause a reallocation, so we can't assume that the original slice refers to the same array as the resulting slice, nor that it refers to a different one.
To use slices correctly, it's important to remember that although the elements of the underlying array are indirect, the slice's pointer, length and capacity are not.
As a result, it's usual to assign the result of a call to append to the same slice variable:
array = append(array, ...)
So to sum up, to receive the desired result always remember to assign the append function to a new or the same slice variable.
Here is the corrected and working code:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
a := testFunction(test.testArray)
fmt.Println(a)
}
func testFunction(array []float64)[]float64 {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
return array
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
Check it the working code on Go Playground.
Another solution is to pass the array argument via pointer reference:
func testFunction(array *[]float64) {
for i:=0; i<3; i++ {
*array = removeFrom(*array, 0)
}
fmt.Println(*array)
}
Go Playground
The slice is a composite type. It has a pointer to the data, the length and the capacity. When you pass it as an argument you're passing those values, the pointer, the length and the capacity; they are copies, always.
In your case you modify the data within the slice when you call removeFrom(), which you can do because you've copied the value of a pointer to the original data into the func, but the length and capacity remain unchanged outside the scope of that function as those are not pointers.
So, when you print it again from main() you see the altered values but it still uses the original length and capacity as any changes made to those within the scope of the other funcs were actually on copies of those values.
Here is a useful blog post about slices https://blog.golang.org/slices. It states this in particular.
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.
The reason you see [40 50 50 50 50] is because you changed the values in the slice, but you did not alter the slice itself(it's cap and len)
Let's say we have this kind of a struct (one of the simplest ever):
type some struct{
I uint32
}
And we want to have a variable of that type and to atomically increment in for loop (possibly in another goroutine but now the story is different). I do the following:
q := some{0}
for i := 0; i < 10; i++ {
atomic.AddUint32(&q.I,1) // increment [1]
fmt.Println(q.I)
}
We're getting what we'd expect, so far so good, but if we declare a function for that type as follows:
func (sm some) Add1(){
atomic.AddUint32(&sm.I,1)
}
and call this function in the above sample (line [1]) the value isn't incremented and we just get zeros. The question is obvious - why?
This has to be something basic but since I am new to go I don't realize it.
The Go Programming Language Specification
Calls
In a function call, the function value and arguments are evaluated in
the usual order. After they are evaluated, the parameters of the call
are passed by value to the function and the called function begins
execution. The return parameters of the function are passed by value
back to the calling function when the function returns.
The receiver sm some is passed by value to the method and the copy is discarded when you return from the method. Use a pointer receiver.
For example,
package main
import (
"fmt"
"sync/atomic"
)
type some struct {
I uint32
}
func (sm *some) Add1() {
atomic.AddUint32(&sm.I, 1)
}
func main() {
var s some
s.Add1()
fmt.Println(s)
}
Output:
{1}
Go Frequently Asked Questions (FAQ)
When are function parameters passed by value?
As in all languages in the C family, everything in Go is passed by
value. That is, a function always gets a copy of the thing being
passed, as if there were an assignment statement assigning the value
to the parameter. For instance, passing an int value to a function
makes a copy of the int, and passing a pointer value makes a copy of
the pointer, but not the data it points to.
Should I define methods on values or pointers?
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
For programmers unaccustomed to pointers, the distinction between
these two examples can be confusing, but the situation is actually
very simple. When defining a method on a type, the receiver (s in the
above examples) behaves exactly as if it were an argument to the
method. Whether to define the receiver as a value or as a pointer is
the same question, then, as whether a function argument should be a
value or a pointer. There are several considerations.
First, and most important, does the method need to modify the
receiver? If it does, the receiver must be a pointer. (Slices and maps
act as references, so their story is a little more subtle, but for
instance to change the length of a slice in a method the receiver must
still be a pointer.) In the examples above, if pointerMethod modifies
the fields of s, the caller will see those changes, but valueMethod is
called with a copy of the caller's argument (that's the definition of
passing a value), so changes it makes will be invisible to the caller.
By the way, pointer receivers are identical to the situation in Java,
although in Java the pointers are hidden under the covers; it's Go's
value receivers that are unusual.
Second is the consideration of efficiency. If the receiver is large, a
big struct for instance, it will be much cheaper to use a pointer
receiver.
Next is consistency. If some of the methods of the type must have
pointer receivers, the rest should too, so the method set is
consistent regardless of how the type is used. See the section on
method sets for details.
For types such as basic types, slices, and small structs, a value
receiver is very cheap so unless the semantics of the method requires
a pointer, a value receiver is efficient and clear.
Your function need to receive a pointer for the value to be incremented, that way you are not passing a copy of the struct and on next iteration the I can be incremented.
package main
import (
"sync/atomic"
"fmt"
)
type some struct{
I uint32
}
func main() {
q := &some{0}
for i := 0; i < 10; i++ {
q.Add1()
fmt.Println(q.I)
}
}
func (sm *some) Add1(){
atomic.AddUint32(&sm.I,1)
}
I'm creating a priority queue using Go's heap package. There is an example of one in the documentation.
The queue I'm creating needs to be based around a struct rather than a slice because it requires other properties like a mutex.
type PQueue struct {
queue []*Item
sync.Mutex
}
I implement all the methods that heap.Interface requires.
The issue is that my PQueue.Push method seems not to be permanently adding a value to PQueue.queue.
func (p PQueue) Push(x interface{}) {
p.Lock()
defer p.Unlock()
item := x.(*Item)
item.place = len(p.queue) // the index of an item in the queue
p.queue = append(p.queue, item)
// len(p.queue) does increase
// after the functions exits, the queues length has not increased
}
If I print the length of p.queue at the end of this function, the length has increased. After the functions exits however, it seems the original struct does not get updated.
I think it might be happening because of func (p PQueue) not being a pointer. Why might that be? Is there a way to fix it? If I were to use func (p *PQeueue) Push(x interface{}) instead, I would need to implement my own heap because heap.Interface specifically requires no pointer. Is that my only option?
The problem is that you are appending to a copy of your slice. Thus the change shows within the function, but is lost once you return from the function.
In this blog article 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.
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.
As a result you need to pass a pointer if you want to modify it with append. Simply change the method to use a pointer receiver. And for that to work you need to call init with a pointer like heap.Init(&pq) as shown in the example that you linked to which does just that and also uses pointer receivers.
From the spec on Method Sets:
The method set of the corresponding pointer type *T is the set of all methods
declared with receiver *T or T (that is, it also contains the method
set of T).
So using a pointer type will work with value and pointer receivers and still implement the interface.
You are right about the problem being related to the receiver of your Push method: the method will receive a copy of the PQueue, so any changes made to the struct will not persist.
Changing the method to use a pointer as a receiver is the correct change, but this also means that PQueue no longer implements heap.Interface. This is due to the fact that Go does not let you take a pointer to the value stored inside an interface variable, so the automatic translation of q.Push() to (&q).Push() does not occur.
This isn't a dead end though, since *PQueue should still implement the heap.Interface. So if you were previously calling heap.Init(q), just change it to heap.Init(&q).
I think it might be happening because of func (p PQueue) not being a pointer
That's right. Quoting Effective Go:
invoking [the method] on a value would cause the method to receive a
copy of the value, so any modifications would be discarded.
You say:
heap.Interface specifically requires no pointer
I'm confused, the example you point to is, in fact, using a pointer:
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
Maybe something else is going on?
The code is like the following:
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{{"one"},{"two"},{"three"}}
for _,v := range data {
go v.print()
}
time.Sleep(3 * time.Second)
}
I know that the code is wrong,because the for loop variable is reused in the for-range loop.
When the goroutine has got the chance to launch,the value of v might has been modified. so the print result will be "three,three,three".
But when we modify the data variable into another declaration as:
data := []*field{{"one"},{"two"},{"three"}}
the print result will be "one ,two,three".
I didn't get the point of why. Does the pointer make any difference or any different mechanism is on this?
I read this from this article. But the poster didn't not tell why. Or it's just a incident the output is right.
In the first loop, v is the value of a field item. Because v is addressable, it is automatically referenced as the pointer receiver for the print() method. So v.print() is using the address of v itself, and the contents of that address is overwritten each iteration of the loop.
When you change the declaration to use a *field, v is now a pointer to a field value. When you call v.print() in this case, you are operating on the value that v points to, which is stored in data, and the overwriting of v has no effect.