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