-1

I'm trying to wrap my head around embedding in golang, and I am a bit confused when it comes to the state of a type embedded in another.

Here is my question: If I have a type Embedii that is an int, and it has a method that effects its value, should that appear in a type that embeds it?

Here is what I was playing with:

package main

import (
  "fmt"
)

type Embedii int

func (y *Embedii) Do() {
  if y == nil {
    y = new(Embedii)
  } else {
    *y = *y + 1
  }
  fmt.Println(*y)
}

type Embedder struct {
  *Embedii
}

func main() {
  embedii := new(Embedii)
  embedii.Do() // prints 1
  embedii.Do() // prints 2

  fmt.Println("---")

  embedder := new(Embedder)
  embedder.Do() // prints 0
  embedder.Do() // prints 0

  fmt.Println("---")

  nembedii := new(Embedii)
  embedo := &Embedder{nembedii}
  embedo.Do() // prints 1
  embedo.Do() // prints 2
}

https://play.golang.org/p/ArqKESVWoS-

I'm curious to understand why I have to explicitly pass an existing instance of Embedii to the Embedder type for this to work properly

icza
  • 389,944
  • 63
  • 907
  • 827
Tom Klino
  • 2,358
  • 5
  • 35
  • 60
  • 1
    `if y == nil { y = new(Embedii) }` probably doesn't do what you think it does. Do not write Javacode in Go. – Volker Mar 10 '20 at 12:51
  • 1
    Doesn't really resolve your question, but this would achieve all three variants incrementing as expected; https://play.golang.org/p/3aUC412mwsm - Fairly sure your issue is what Volker mentioned. Couldn't say for certain but probably related to remapping the memory address to a new pointer. – Mikey Mar 10 '20 at 12:58
  • @Mikey that's very interesting, now what I'm curios about is how come the methods of `Embedii` (which are defined with a pointer reference) are added to `Embedder` even though the receiver is a pointer and `Embedii` is included in the `Embedder` struct without the `*`. – Tom Klino Mar 10 '20 at 13:13

2 Answers2

4

In Embedii.Do() the receiver is a pointer value. It's a copy. Assigning anything to this pointer variable just modifies the copy.

y = new(Embedii) just assigns a pointer to the local variable y, and when Do() returns, it's lost. When called again, y will be nil again, so it creates and assigns a new value to it (which will be lost again after returning).

It works if you create an Embedii prior, because then you don't create and assign it in Do() (which would be lost).

If you return the new Embedii (more precisely its address) and assign it, you will see it incrementing, but it will start with 0 not 1, because the first call just creates it without incrementing, and in the other cases it already existed and so first call increments right away:

func (y *Embedii) Do() *Embedii {
    if y == nil {
        y = new(Embedii)
    } else {
        *y = *y + 1
    }
    fmt.Println(*y)
    return y
}

And using it:

embedder := new(Embedder)
embedder.Embedii = embedder.Do() // prints 0
embedder.Embedii = embedder.Do() // prints 1

Output will be (try it on the Go Playground):

1
2
---
0
1
---
1
2
icza
  • 389,944
  • 63
  • 907
  • 827
0

"should that appear in a type that embeds it?" yes it should, because it is another type inside the struct.

Adriel Artiza
  • 327
  • 4
  • 12