我刚刚和Exercise 51 in the Tour of Go一起玩。该解释声明Scale
方法在收到Vertex
而不是指向Vertex
的指针时无效。
然而,当我在v := &Vertex{3, 4}
中将声明v := Vertex{3, 4}
更改为main
时,输出中唯一的变化就是缺少标记指针的&
。
那么为什么Scale
改变它收到的变量,即使变量不是指针?
答案 0 :(得分:8)
它没有“收到”一个值。 Go是强类型的,所以如果在某处指定了指向T的指针,则指向T(*T
)的指针是唯一可以作为此类型位置的值的选项。
“魔术”在编译器中,可以在某些conditions下有效地“重写”您的代码:
如果方法集{(1)}的
x.m()
包含x
并且参数列表可以分配给{{1}的参数列表,则方法调用m
有效}。如果m
是可寻址的且& x的方法集包含x
,则m
是x.m()
的简写:
相关:Method sets
答案 1 :(得分:1)
巡回建议的差异实际上并不是在将v := &Vertex{3, 4}
更改为v:= Vertex{3, 4}
,而是更改两种方法的定义,以便它们可以处理值而不是指针。因此,例如,对于Scale
,func (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
。