"不能取地址"和"不能调用指针方法"

hun*_*tis 12 methods struct pointers go

这编译和工作:

diff := projected.Minus(c.Origin)
dir := diff.Normalize()
Run Code Online (Sandbox Code Playgroud)

这不会(产生标题中的错误):

dir := projected.Minus(c.Origin).Normalize()
Run Code Online (Sandbox Code Playgroud)

有人可以帮我理解为什么吗?(学习Go)

以下是这些方法:

// 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}
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 23

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

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

(&diff).Normalize()
Run Code Online (Sandbox Code Playgroud)

详见规范:电话:

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

你的第二个例子不起作用的原因是因为函数和方法调用的返回值不可寻址,所以编译器不能在这里做同样的事情,编译器无法获取返回值的地址Vector3.Minus()呼叫.

什么是可寻址的确切列在Spec:Address运算符中:

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

查看相关问题:

如何从函数调用中获取返回值的指针?

如何在Go中存储对操作结果的引用?

可能的"变通办法"

"最简单"(需要最少的更改)只是分配给变量,然后调用该方法.这是您的第一个工作解决方案.

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

另一种方法是修改返回值以返回指针(*Vector3)而不是Vector3.如果返回值已经是指针,则不需要获取其地址,因为接收器对于需要指针接收器的方法来说是好的.

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

func pv(v Vector3) *Vector3 {
    return &v
}
Run Code Online (Sandbox Code Playgroud)

使用它:

dir := pv(projected.Minus(c.Origin)).Normalize()
Run Code Online (Sandbox Code Playgroud)

这也可以是一种方法Vector3,例如:

func (v Vector3) pv() *Vector3 {
    return &v
}
Run Code Online (Sandbox Code Playgroud)

然后使用它:

dir := projected.Minus(c.Origin).pv().Normalize()
Run Code Online (Sandbox Code Playgroud)

一些说明:

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


小智 5

接受的答案真的很长,所以我只想发布对我有帮助的内容:

我收到了关于这一行的错误:

services.HashingServices{}.Hash("blabla")
Run Code Online (Sandbox Code Playgroud)

所以我只是将其更改为:

(&services.HashingServices{}).Hash("blabla")
Run Code Online (Sandbox Code Playgroud)