Memory, scope, and function returns

Dear All,

This is my first post here, so please be gentle. I am a new Go coder, having been a C++ coder about 20 years ago, and conducted another career since then. I have only been coding Go for about 1.5 months, and only part time, so I have not seen EVERY tutorial out there. Google sends me to pages that are a bit shallow for this question (which is STILL just a NOOB question, sorry!), so I thought I needed to ask real people.

So, I have two questions about the code which follows. This code is a (much) simplified version of my actual code, but, it has the same bad behaviour I do not understand/want.

  1. First question (less important): How come I cannot assign my pointers directly to addresses of objects returned by functions? E.g., lines 44-47 of my code below COULD be

    dss.dOnep := &NewDude(“Albert”)
    dss.dTwop := &NewDude(“Bella”)

but that does not seem to work (while my given code DOES work).

  1. More importantly, how come in the final code, the Inversion does not “stick”? That is, I thought I was working directly on the memory of `myDudes’ from line 65 in the call to Invert on line 67, but we can see that while the right thing is happening inside the Invert() function call (see my playground output), on return the order switches back.

Here is the Code:

================================
package main

import (
“fmt”
)

type dudeStruct struct {
dude string
}

// Dude is an interface for the dudeStruct.
// a Dude can be created and gives a HelloString().
type Dude interface {
HelloString() string
}

// NewDude makes a pointer to a new Dude, with given name.
func NewDude(name string) Dude {
var ds dudeStruct
ds.dude = name
return ds
}

// String gives a Dude printString for a dudeStruct object.
func (ms dudeStruct) String() string {
return "Hello from " + ms.dude + ". "
}

// Now a collection of two dudes
type dudesStruct struct {
dOnep *Dude
dTwop *Dude
}

// Dudes is an interface to two dudes as an ordered set.
type Dudes interface {
String() string
Invert()
}

// MakeDudes does what it says. Returns pointer to the Dudes object created.
func MakeDudes(dOne, dTwo string) Dudes {
var dss dudesStruct
albert := NewDude(“Albert”)
bella := NewDude(“Bella”)
dss.dOnep = &albert
dss.dTwop = &bella
return &dss
}
// String gives a Dudes printstring for a dudesStruct object.
func (dss dudesStruct) String() string {
return ((*dss.dOnep).HelloString() + (*dss.dTwop).HelloString())
}
// Invert SHOULD switch the order of the dudes. It does internally, but not after-the-fact.
func (dss dudesStruct) Invert() {
fmt.Println("In Invert() – before inversion: " + dss.String() + "'") swapSpacep := dss.dOnep dss.dOnep = dss.dTwop dss.dTwop = swapSpacep fmt.Println("In Invert() -- after inversion: " + dss.String() + “’”)
}

func main() {
var myDudes Dudes
myDudes = MakeDudes(“Alice”, “Bob”)
fmt.Println(myDudes.String())
myDudes.Invert()
fmt.Println(myDudes.String())
}

============================
Here is the output from Go Playground:


Hello from Albert. Hello from Bella.

In Invert() – before inversion: `Hello from Albert. Hello from Bella. ’

In Invert() – after inversion: `Hello from Bella. Hello from Albert. ’

Hello from Albert. Hello from Bella.


Clearly, as is right, I want Bella first!!

Also, any (gentle) tips on anything I am doing stupidly would be greatly appreciated.

Firstly, welcome to the forum!

Secondly, Please put three “backticks” before and after your code when you post it so it’s easier to read. For example:

```
code here
```

Now, onto your questions!

I started off writing answers but they started turning int long paragraphs, so I felt it might be better to make annotated changes to the code itself: https://play.golang.org/p/LglFe0kbnaI

I’d say the root of your problem is that you were passing struct values that implement your Dude and Dudes interfaces (which was fine for dudeStruct, but not for dudesStruct) but then using pointers to your interfaces. A pointer to an interface is not the same as a pointer to the value wrapped within the interface. One thing that threw me off was when I saw *Dude and (*dss.dOnep).HelloString() Don’t take this personally because it’s not specific to your code, but whenever I see a pointer to an interface, I’m highly suspicious that there’s something wrong. You could pass a pointer to your dudeStruct in order to implement the Dude interface, but you shouldn’t pass a pointer to your Dude interface. That’s why you had to have that explicit pointer dereference before calling HelloString.

1 Like

Thanks, @skillian,

That is very helpful, and of course I can get my code to do what I wanted now, and have various restructurings/refactorings to do now as well.

That said, there are several points I still don’t understand: that is, I can “do it”, but I do not yet “dig it”.

First Question:

  • The return type of the function MakeDudes is Dudes:
func MakeDudes(dOneName, dTwoName string) Dudes {
	return &dudesStruct{
		dOne: NewDude(dOneName),
		dTwo: NewDude(dTwoName),
	}
}

but we are actually returning the address of the dudesStruct. I get that I can think of this as a mechanism to implement polymorphism, but I do not understand the technical language justification for using a pointer in the body but not in the stated return type. In the Stack memory is allocated to MakeDudes, and in that the local dudeStruct is created. In the return statement, either we are returning a copy of the pointer to the address of the created copy (so on my machine, a 64 bit int, in playground a 32 bit int) which will then be preserved by GC until it falls out of use, or we are building a whole copy of the dudesStruct boject somewhere (I would have said on the Heap, but we are claiming to return a thing and not a pointer to a thing, so I am not certain). Maybe we are not returning a Thing since we are returning an interface?

Second Question:

  • In your InvertInPlace
// InvertInPlace inverts its two people.
func (dss *dudesStruct) InvertInPlace() {
	dss.dOne, dss.dTwo = dss.dTwo, dss.dOne
}

From what I can see, the main difference to what I had is that the function hook func (dss *dudesStruct) InvertInPlace() uses a pointer to the underlying object calling the function, and not the object itself. This REALLY confuses me. If I called the function with this code instead:

// InvertInPlace inverts its two people.
func (dss dudesStruct) InvertInPlace() {
	dss.dOne, dss.dTwo = dss.dTwo, dss.dOne
}

I would have thought that the function is acting on the actual calling object X and not a copy. I am very confused by this. From our discussion, I am guessing that this code actually creates a whole copy of Y on the Stack memory allocated to the function call, and then the inversion happens in this copy, and then that copy Y would just be released afterwards to the GC? (So, X is unaffected, as in my original code)?

Also, thank you for reminding me about the swap mechanism within Go language which simplifies my traditional swapping mechanism:

dss.dOne, dss.dTwo = dss.dTwo, dss.dOne

I had read about this somewhere, but I had not yet integrated it into my progress in learning to code in Go.

Thanks, altogether!!

So it sounds like there might be some confusion between Go’s interface types vs. concrete types. Interfaces are pretty obvious because their definitions have interface in them. Concrete types are everything else (e.g. dudeStruct, *dudeStruct, string, etc.). Interfaces are essentially wrappers around a concretely-typed value. When you return a dudesStruct value, but the function return type is a Dudes interface, the dudesStruct is allocated on the heap, boxed into a (type, data) pointer pair (that’s how Go interfaces are currently implemented), and then that pointer pair is returned as a Dudes interface.

By returning a pointer to the dudesStruct instead of its value, that type pointer in the MakeDudes return value is different: Instead of pointing to dudesStruct’s type information, it’s now pointing to *dudesStruct’s type information.

Pointers are the most common way to get indirection so that you can mutate the target of the pointer. The (*dudeStruct).InvertInPlace function needs to be able to mutate the fields within dss, so it needs to be a pointer. Otherwise, it is a copy, because that’s what passing a struct by value mean: Copying it.

Correct.

Explicit function parameters in Go are always pass by value. Method receivers are still function parameters; as a matter of fact, you could call dudesStruct.InvertInPlace like this:

func main() {
    var ds dudesStruct
    (*dudesStruct).InvertInPlace(&ds)

    // and you could call `Inverted` like this:
    dudes := (dudeStruct).Inverted(ds)
}

Note that just because you can call methods like this, don’t. I’m just demonstrating that there’s no special “this” parameter that is a special “reference” type like it is in C#, C++ or Java, etc.

Thanks, @skillian! I think I understand now.

Trying to understand how go handles memory `under the hood’ is a little difficult using most of the beginning tutorials I have seen, where things are just like do this and this will happen. Your comments have helped me a lot. :slight_smile: