实现Mixins和编译器行为不一致

Mixins can be implemented in Go (1.4.1) using embedding and since struct{} occupies no memory (as I understand) it fits for the situations that we want to add some functionality or just add a method to a type that may actually has nothing to do with it's state, but we like to avoid ParseThing(...) and instead write thing.Parse(...).

So having:

type X struct{}

func (x X) F() {
    fmt.Println("functionality in X.F()")
}

type Y struct{ X }
type Z struct{ Y }

Then if we do:

var z Z
z.F()

Will give us:

functionality in X.F()

So far so good.

Now let's add another type OX with method F() and embed it in Z:

type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) F() {
    fmt.Println("functionality in OX.F()")
}

Interesting! Now we get functionality in OX.F() which shows us that Go compiler searches for the method, starting from type it self and then the last embedded type. We can check that by adding F() to Z:

func (x Z) F() {
    fmt.Println("functionality in Z.F()")
}

The output is functionality in Z.F(). Now if we remove the Z.F() method and add F() to Y:

//func (x Z) F() {
//    fmt.Println("functionality in Z.F()")
//}

func (x Y) F() {
    fmt.Println("functionality in Y.F()")
}

Then we see this error ambiguous selector z.F; redirecting via pointers makes no difference.

Question 1: Why that's so?

The extra level of indirection Y meant for something else, but brought me to this. And as I've guessed func (t T) String() string{} is an exception. This code:

type X struct{}

func (x X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) String() string {
    return "in OX.String()"
}

func (x Y) String() string {
    return "in Y.String()"
}

And then this:

var z Z
fmt.Println(z)

Gives us:

{in Y.String() in OX.String()}

Which is logical. But if we use pointer receivers:

import (
    "fmt"
    "testing"
)

func TestIt(t *testing.T) {
    var z Z
    fmt.Println(z)
}

type X struct{}

func (x *X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x *OX) String() string {
    return "in OX.String()"
}

func (x *Y) String() string {
    return "in Y.String()"
}

Will print out:

{{{}} {}}

Question 2: Why is that so?

Question 1

The compiler is correct. How should it decide, which of OX.F and Y.F should it use? It can't. So it's up to you to call the desired method directly: either with

z.Y.F()

or

z.OX.F()

Edit: As for why your example worked until you've defined F on Y, this is mentioned in the Spec:

For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.

(Emphasis added.)

Before you defined the method, the shallowest implementation was OX.F. After you've defined Y.F, there became two Fs on the same level, which is illegal.

Question 2

Again, the compiler is correct. You have embedded types Y and OX into Z, not *Y and *OX. As written in the Spec,

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).

*T has all methods of T, but not the other way around. Methods sets of OX and Y are empty, so obviously, fmt.Println just prints them as if they were any other kind of struct with no String() method defined.

Ainar-G write neat answer

Spec:

For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.

I'd like to add a bit

Spec:

If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

So things would work if you just use referencing to promote methods like

fmt.Println(&z)

But this will cause ambiguity in selection cause of there are few possibilities for String method and so selector String is illegal due to spec. Compiler must complain, but it doesn't. This behaviour looks unspecified and can be only explained as special case for common printing operation to my mind. This will work as expected

var y Y
fmt.Println(&y)

Here is working example Playground