为什么一个带有指针接收器的方法在接收一个值时仍然有效?

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

Why does a method with a pointer receiver still work when it receives a value?

问题

我刚刚在Go之旅中的第51个练习中玩耍。解释声称当Scale方法接收到一个Vertex而不是一个指向Vertex的指针时,它没有任何效果。

然而,当我在main中将声明v := &Vertex{3, 4}更改为v := Vertex{3, 4}时,输出中唯一的变化是缺少了&来标记指针。

那么为什么Scale会改变它接收到的变量,即使该变量不是一个指针呢?

英文:

I was just playing with Exercise 51 in the Tour of Go. The explanation claims the Scale method has no effect when it receives a Vertex instead of a pointer to a Vertex.

Yet when I change the declaration v := &Vertex{3, 4} to v := Vertex{3, 4} in main the only change in the output is the missing & to mark the pointer.

So why does Scale change the variable it receives even if the variable isn't a pointer?

答案1

得分: 8

它不“接收”一个值。Go是强类型的,所以如果某处规定了一个指向T的指针,那么指向T(*T)是唯一可能作为这种类型位置的值的选项。

“魔法”在于编译器,在某些条件下有效地“重写”你的代码:

如果方法集(x的类型)包含m,并且参数列表可以赋值给m的参数列表,则方法调用x.m()是有效的。如果x是可寻址的,并且&x的方法集包含m,则x.m()(&x).m()的简写形式:

相关:方法集

英文:

It does not "receive" a value. Go is strongly typed, so if somewhere a pointer to T is prescribed, a pointer to T (*T) is the only option which can happen as a value for such typed place.

The "magic" is in the compiler which effectively "rewrites" your code under certain conditions:

> A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m():

Related: Method sets

答案2

得分: 1

这个差异并不是将v := &Vertex{3, 4}改为v:= Vertex{3, 4},而是改变了两个方法的定义,使其在值上工作而不是指针。所以,例如对于Scalefunc (v *Vertex) Scale(f float64) {...变成了func (v Vertex) Scale(f float64) {...(注意(v *Vertex),一个指针值,变成了(v Vertex),一个非指针值)。在这两种情况下,你应该将v的声明保持为v := &Vertex{3, 4}

你会注意到,在第一种情况下,当方法接受指针时,输出是&{15 20} 25。然而,当方法接受值而不是指针时,输出是&{3 4} 5

在这两种情况下,v都是一个指向Vertex对象的指针。在第一种情况下,指针被传递给方法,一切都按预期工作 - 对Vertex对象所做的任何修改都是对原始值的修改,因此这些更改在方法返回后仍然存在。然而,在第二种情况下,尽管v仍然是一个指针,但Go编译器足够聪明,将v.Scale(5)转换为(*v).Scale(5),其中v被解引用,并将结果值传递给Scale

英文:

The difference that the tour suggests isn't actually in changing v := &Vertex{3, 4} to v:= Vertex{3, 4}, but rather in changing the definitions of the two methods so that they work on values instead of pointers. So, for example, for Scale, func (v *Vertex) Scale(f float64) {... becomes func (v Vertex) Scale(f float64) {... (note that (v *Vertex), a pointer value, becomes (v Vertex), a non-pointer value). In both cases, you should leave the declaration of v as v := &Vertex{3, 4}.

You'll notice that in the first case, when the methods take pointers, the output is &{15 20} 25. However, when the methods take values rather than pointers, the output is &{3 4} 5.

In both cases, v is a pointer to a Vertex object. In the first case, the pointer is passed to the methods, and everything works as expected - any modifications made to the Vertex object are made to the original value, so those changes persist after the method returns. In the second case, though v is still a pointer, the Go compiler is smart enough to convert v.Scale(5) to (*v).Scale(5), where v is dereferenced, and the resulting value is passed to Scale.

huangapple
  • 本文由 发表于 2013年8月1日 22:24:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/17996771.html
匿名

发表评论

匿名网友

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

确定