-2

I have the following code. https://play.golang.org/p/YAa6cgtA3Vo

The address of the receiver pointer varies between method calls. This is perplexing. Can anyone explain why this is the case? Do I need to pass a pointer to a receiver as an argument in order to maintain the same address?

type myStruct struct {
    //struct content is irrelevant
}

func (ptrToStruct *myStruct) returnAddress() **myStruct {
    return &ptrToStruct
}

func (ptrToStruct *myStruct) method1() {
    addressOfPtr := ptrToStruct.returnAddress()
    fmt.Println(&ptrToStruct)
    fmt.Println(addressOfPtr)
    if &ptrToStruct != addressOfPtr {
        fmt.Println("Different addresses!")
    }
}

EDIT:

What I want is the address of ptrToStruct and not its value. I know that I could just get it in method1() by typing addressOfPtr = &ptrToStruct but in my use case I have some logic happening in the returnAddress() method and I need it to come from there.

Peter
  • 119
  • 12

1 Answers1

2

That's simple: when you have var ms myStruct somewhere, calling ms.returnAddress() would pass the address of the ms variable to returnAddress, and that address would always be the same (this is easily verifyable — try it itself).

Now consider that the ptrToStruct in the returnAddress definition is just a special argument to be passed to that method, and it has a concrete type — *myStruct.

This type internally is just an integer — large enough to store an address of any memory location on your platform/OS combination.

Now consider that when the call ms.returnAddress() is performed, the address of the ms variable is taken and written to the ptrToStruct argument passed to returnAddress. And that argument is also a variable — with the lifetime equal to the duration of the function call, and visible only in that function's body.

This variable is automatically created on the call stack by the code which prepares the function call. (And the same happens to all the other method arguments.)

So, when you take the address of ptrToStuct, you take the address of a variable appeared at some location on the call stack which will be gone once the method returns.

Now consider that:

  • goroutine stacks in Go are growable (and hence may be relocated in memory when grown);
  • a method may be called from different goroutines (each has its own stack);
  • even if the method is called from the same goroutine multiple times, it may be called from different places in the code path executed by that goroutine.

All of the above may lead to the address of ptrToStruct variable being essentially random from call to call.

What you (probably) actually want is just returning the value of ptrToStruct as is, not its address.

If you feel like not really getting into how ptrToStruct springs into existence and vanishes, consider starting with this.

halfer
  • 19,824
  • 17
  • 99
  • 186
kostix
  • 51,517
  • 14
  • 93
  • 176
  • What I don't understand is your first paragraph. You say that if I call ```ms.returnAddress()``` that I would receive the actual address (always the same). How is this reconcilable with the rest of your response? Thank you – – Peter May 16 '19 at 17:41
  • The method `myStruct.returnAddress` is internally a plain function with the single argument, `ptrToStruct` of type `*myStruct`. When the compiler encounters `ms.returnAddress()` in the code it sees that the method was declared on a pointer (and not on plain value) and so it generates machine code which performs the three steps: 1) arranges for the so-called stack frame to be created for the call; 2) take the address of `ms`, and place it into the call frame; 3) actually call the function `returnAddress`. The latter sees the value created at step 2 as its method receiver. Is that clear? – kostix May 16 '19 at 18:11
  • 1
    @Peter if you're not sure why (2) happens automatically, this is by [the spec](https://golang.org/ref/spec#Calls), which reads: «A method call `x.m()` is valid if the method set of (the type of) `x` contains `m` and the argument list can be assigned to the parameter list of `m`. If `x` is addressable and `&x`'s method set contains `m`, `x.m()` is shorthand for `(&x).m()`». So basically nothing prevents you from explicitly calling that method on the address of that `ms` variable: `(&ms).returnAddress()`. It's just tedious and verbose, and it's easily handled by the compiler itself. – kostix May 16 '19 at 18:15