Pointers &引用;不能使用“的地址”;及;无法对“”调用指针方法;

Pointers &引用;不能使用“的地址”;及;无法对“”调用指针方法;,pointers,go,methods,struct,Pointers,Go,Methods,Struct,这将编译并运行: diff := projected.Minus(c.Origin) dir := diff.Normalize() 这不会(在标题中产生错误): 有人能帮我理解为什么吗?(学习围棋) 以下是这些方法: // Minus subtracts another vector from this one func (a *Vector3) Minus(b Vector3) Vector3 { return Vector3{a.X - b.X, a.Y - b.Y, a.Z -

这将编译并运行:

diff := projected.Minus(c.Origin)
dir := diff.Normalize()
这不会(在标题中产生错误):

有人能帮我理解为什么吗?(学习围棋)

以下是这些方法:

// Minus subtracts another vector from this one
func (a *Vector3) Minus(b Vector3) Vector3 {
    return Vector3{a.X - b.X, a.Y - b.Y, a.Z - b.Z}
}

// Normalize makes the vector of length 1
func (a *Vector3) Normalize() Vector3 {
    d := a.Length()
    return Vector3{a.X / d, a.Y / d, a.Z / d}
}

Vector3.Normalize()
方法有一个指针接收器,因此要调用此方法,需要指向
Vector3
值的指针(
*Vector3
)。在第一个示例中,将
Vector3.Minus()的返回值存储在变量中,该变量的类型为
Vector3

Go中的变量是可寻址的,当您编写
diff.Normalize()
时,这是一个快捷方式,编译器将自动获取
diff
变量的地址,以获得所需的
*Vector3
类型的接收器值,以便调用
Normalize()
。因此编译器将把它“转换”为

详细信息请参见

如果of(类型)
x
包含
m
,并且可以将参数列表指定给
m
的参数列表,则方法调用
x.m()
有效。如果
x
是且
&x
的方法集包含
m
x.m()
(&x.m()
的缩写

第二个示例不起作用的原因是函数和方法调用的返回值是不可寻址的,因此编译器无法在此处执行相同的操作,编译器无法获取
Vector3.减号()的返回值的地址

可寻址的内容在

操作数必须是可寻址的,即变量、指针间接寻址或切片索引操作;或可寻址结构操作数的字段选择器;或可寻址数组的数组索引操作。作为可寻址性要求的例外,
x
[在
&x
]的表达式中]也可以是a(可能带括号)

见相关问题:

可能的“变通办法” “最简单的”(需要最少的更改)就是简单地分配一个变量,然后调用该方法。这是您的第一个工作解决方案

另一种方法是修改方法,使其具有值接收器(而不是指针接收器),这样就不需要获取方法返回值的地址,因此可以“链接”调用。请注意,如果一个方法需要修改接收器,这可能是不可行的,因为只有当它是指针时才可能(因为接收器与任何其他参数一样通过复制来传递,如果它不是指针,则只能修改副本)

另一种方法是将返回值修改为返回指针(
*Vector3
),而不是
Vector3
。如果返回值已经是一个指针,则无需使用其地址,因为对于需要指针接收器的方法来说,它与接收器一样好

您还可以创建一个简单的helper函数来返回其地址。它可能看起来像这样:

func pv(v Vector3) *Vector3 {
    return &v
}
使用它:

dir := pv(projected.Minus(c.Origin)).Normalize()
这也可能是向量3的一种方法,例如:

func (v Vector3) pv() *Vector3 {
    return &v
}
然后使用它:

dir := projected.Minus(c.Origin).pv().Normalize()
一些注意事项:


如果您的类型仅由3个
float64
值组成,则不应看到显著的性能差异。但是你应该对你的接受者和结果类型保持一致。如果大多数方法都有指针接收器,那么所有方法都应该有指针接收器。如果您的大多数方法都返回指针,那么所有方法都应该返回指针。

接受的答案非常长,因此我将发布帮助我的内容:

关于这一行,我遇到了以下错误:

services.HashingServices{}.Hash("blabla")
所以我把它改成:

(&services.HashingServices{}).Hash("blabla")

谢谢,这很有道理。选项为1)不这样做(先分配,然后是地址)或2)使用值接收器是否正确?如果我决定使用选项2,有哪些折衷方案?(考虑到Vector3结构只有3个float64,值拷贝时间不是什么大问题,还有其他我没有想到的缺点吗?)回答不错@icza。但为什么分配给变量有效而链接无效呢?我知道变量是可寻址的,但如果返回值也被自动寻址以便于链接,那不是更好吗?@ManojSuthar是的,在某些情况下这会很方便,但会限制编译器的操作。请参阅返回值可能没有永久地址。如果允许它获取返回值的地址,则需要为堆上的返回值分配内存,这当然效率较低。
services.HashingServices{}.Hash("blabla")
(&services.HashingServices{}).Hash("blabla")