Function assignments of an interface of a structure pointer with a structure show different values

Bog*_*dan 2 methods struct interface go

I'm a bit confused with the next part of the code:

type Tiger struct {
    weight int
}

func (t Tiger) Weight() int {
    return t.weight
}

type AsianTiger struct {
    Tiger
}

type Tigers interface {
    Weight() int
}

func main() {
    t := &AsianTiger{Tiger{3}}

    var i Tigers = t

    f := t.Weight
    g := i.Weight

    t.weight = 7

    fmt.Println(f(), g()) // result is: 3 7 | expected 7 7

    z := t
    x := i

    t.weight = 8

    fmt.Println(z.Weight(), x.Weight()) // result is: 8 8

}
Run Code Online (Sandbox Code Playgroud)

Could someone help me please to understand what is happening with f := t.Weight and g := i.Weight why did they show different results when I expected similarity, and what is happening under the hood?

icz*_*cza 5

The source of the problem / confusion lies in these lines:

f := t.Weight
g := i.Weight
Run Code Online (Sandbox Code Playgroud)

These are method values. A method value saves the receiver in the result function value! And your Tiger.Weight() method has value receiver (not pointer receiver):

func (t Tiger) Weight() int {
    return t.weight
}
Run Code Online (Sandbox Code Playgroud)

This means that t.Weight will save the receiver t, which is a Tiger struct, and along with it the current weight=3 field. (Note: it's true that in f:= t.Weight t is an *AsianTiger value, but t.Weight is a promoted method from the embedded Tiger type, so t.Weight effectively means / denotes the AsianTiger.Tiger.Weight() method whose receiver is a Tiger struct value.)

The method value i.Weight on the other hand is a method of Tigers, the interface, and the concrete value wrapped in it is of type *AsianTiger, a pointer. So the pointer value will be saved.

Next you change the weight:

t.weight = 7
Run Code Online (Sandbox Code Playgroud)

This will not effect f, because the Tiger struct value (the receiver) is saved, it's a copy. This will however effect g, because the saved receiver there is Tigers which holds *AsianTiger, and you change the pointed value (not the pointer).

You can see if you add another line:

fmt.Println(f(), g())               // result is: 3 7 | expected 7 7
fmt.Println(t.Weight(), i.Weight()) // result is: 7 7
Run Code Online (Sandbox Code Playgroud)

This outputs (try it on the Go Playground):

3 7
7 7
Run Code Online (Sandbox Code Playgroud)

The second line prints 7 7 because not a saved receiver value is used, but the actual value of t (and i which points to t).

The remaining of your code:

z := t
x := i

t.weight = 8

fmt.Println(z.Weight(), x.Weight()) // result is: 8 8
Run Code Online (Sandbox Code Playgroud)

Here z and x are not method values. z is a copy of t which is a pointer, and x is a copy of i, an interface value which wraps a pointer. And you change the pointed value (a field of a field of the pointed value). And last you call the methods of z and x, no saved receiver takes place, receivers are evaluated after the change.