Why does GO compiler add a pointer value receiver method to the method set of non-pointer type when using the pointer Type Embedding?

The title might be a little confusing because I’m not sure how to explain in one sentence (one question) without an example and a more detailed story.

To clearly explain what the question in the title means, let me start from a situation that behaves as I expect.

package main

import (
    "fmt"
    "reflect"
)

type MyType1 struct {
    MyType2
}

type MyType2 struct {
}

func (MyType2) Function1() {
}

func (*MyType2) Function2() {
}

func main() {
    t1 := reflect.TypeOf(MyType1{})
    t2 := reflect.TypeOf(&MyType1{})

    fmt.Println(t1, "has", t1.NumMethod(), "methods:")
    for i := 0; i < t1.NumMethod(); i++ {
        fmt.Print(" method#", i, ": ", t1.Method(i).Name, "\n")
    }

    fmt.Println(t2, "has", t2.NumMethod(), "methods:")
    for i := 0; i < t2.NumMethod(); i++ {
        fmt.Print(" method#", i, ": ", t2.Method(i).Name, "\n")
    }
}

When the type embedding is based on “type” (not correct name, but let’s call it like that) everything behaves as I expect. The structure MyType1 has a MyType2 embedded field (“type” embedded field) so the type MyType1 will have the method (MyType1)Function1() in its method set and the type *MyType1 will have the methods (*MyType1)Function1() and (*MyType1)Function2() in its method set. So, each type (MyType1 and *MyType1 ) will get their corresponding methods. *MyType1 will get the method (*MyType1)Function1() because it’s implicitly arises from (MyType1)Function1() . So, as I said before, everything is expected. To prove this, I also used the standard “reflect” package and got the following printout:

main.MyType1 has 1 methods:
 method#0: Function1
*main.MyType1 has 2 methods:
 method#0: Function1
 method#1: Function2

A strange behavior occurs when I replace the “type” embedded field with a “pointer type” embedded field (MyType1 now has *MyType2 instead of MyType2 ). So the code looks like this:

package main

import (
    "fmt"
    "reflect"
)

type MyType1 struct {
    *MyType2
}

type MyType2 struct {
}

func (MyType2) Function1() {
}

func (*MyType2) Function2() {
}

func main() {
    t1 := reflect.TypeOf(MyType1{})
    t2 := reflect.TypeOf(&MyType1{})

    fmt.Println(t1, "has", t1.NumMethod(), "methods:")
    for i := 0; i < t1.NumMethod(); i++ {
        fmt.Print(" method#", i, ": ", t1.Method(i).Name, "\n")
    }

    fmt.Println(t2, "has", t2.NumMethod(), "methods:")
    for i := 0; i < t2.NumMethod(); i++ {
        fmt.Print(" method#", i, ": ", t2.Method(i).Name, "\n")
    }

}

Now what is actually the crux of the problem. The prints is:

main.MyType1 has 2 methods:
 method#0: Function1
 method#1: Function2
*main.MyType1 has 2 methods:
 method#0: Function1
 method#1: Function2

So, somehow MyType1 also has method (MyType1)Function2() even though it is not declared as value receiver method for type MyType2.

Does anyone have any logical explanation as to why this is happening?

Hi @Grujic_Filip,

In your second case, the methods of the embedded pointer field get promoted according to the language spec:

  • If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.