结构指针与结构的接口的函数赋值显示不同的值

huangapple go评论85阅读模式
英文:

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

问题

我对代码的下一部分有点困惑:

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()) // 结果是:3 7 | 预期结果是 7 7

    z := t
    x := i

    t.weight = 8

    fmt.Println(z.Weight(), x.Weight()) // 结果是:8 8

}

有人能帮我理解一下 f := t.Weightg := i.Weight 发生了什么,为什么它们显示不同的结果,而我预期它们应该是相同的,底层发生了什么?

英文:

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

}

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?

答案1

得分: 5

问题/困惑的根源在于以下代码行:

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

这些是方法值。**方法值会将接收器保存在结果函数值中!**而你的Tiger.Weight()方法具有值接收器(而不是指针接收器):

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

这意味着t.Weight将保存接收器t,它是一个Tiger结构体,并且连同当前的weight=3字段一起保存。(注意:在f:= t.Weight中,t是一个*AsianTiger值,但t.Weight是从嵌入的Tiger类型中提升的方法,所以t.Weight实际上表示AsianTiger.Tiger.Weight()方法,其接收器是一个Tiger结构体值。)

另一方面,方法值i.WeightTigers接口的方法,其中包装的具体值的类型是*AsianTiger,一个指针。因此,指针值将被保存。

接下来,你改变了权重:

t.weight = 7

这不会影响f,因为保存的是Tiger结构体值(接收器),它是一个副本。但这会影响g,因为保存的接收器是Tigers,它持有*AsianTiger,而你改变的是指向的值(而不是指针)。

如果你添加另一行代码,你会看到:

fmt.Println(f(), g())               // 输出结果为:3 7 | 预期结果为 7 7
fmt.Println(t.Weight(), i.Weight()) // 输出结果为:7 7

这将输出(在Go Playground上尝试一下):

3 7
7 7

第二行打印的是7 7,因为它使用的不是保存的接收器值,而是实际的t值(以及指向ti)。

你代码的剩余部分:

z := t
x := i

t.weight = 8

fmt.Println(z.Weight(), x.Weight()) // 输出结果为:8 8

在这里,zx不是方法值。zt的副本,它是一个指针,而xi的副本,它是一个包装指针的接口值。然后你改变了指向的值(指向值的字段)。最后,你调用了zx的方法,没有保存的接收器发生作用,接收器在改变之后进行评估。

英文:

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

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

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
}

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

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

This outputs (try it on the Go Playground):

3 7
7 7

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

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.

huangapple
  • 本文由 发表于 2022年8月22日 20:40:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/73445297.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定