Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"cannot take the address of" and "cannot call pointer method on"

Tags:

This compiles and works:

diff := projected.Minus(c.Origin) dir := diff.Normalize() 

This does not (yields the errors in the title):

dir := projected.Minus(c.Origin).Normalize() 

Can someone help me understand why? (learning Go)

Here are those methods:

// 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} } 
like image 584
hunterloftis Avatar asked Jun 14 '17 11:06

hunterloftis


2 Answers

The Vector3.Normalize() method has a pointer receiver, so in order to call this method, a pointer to Vector3 value is required (*Vector3). In your first example you store the return value of Vector3.Minus() in a variable, which will be of type Vector3.

Variables in Go are addressable, and when you write diff.Normalize(), this is a shortcut, and the compiler will automatically take the address of the diff variable to have the required receiver value of type *Vector3 in order to call Normalize(). So the compiler will "transform" it to

(&diff).Normalize() 

This is detailed in Spec: Calls:

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().

The reason why your second example doesn't work is because return values of function and method calls are not addressable, so the compiler is not able to do the same here, the compiler is not able to take the address of the return value of the Vector3.Minus() call.

What is addressable is exactly listed in the Spec: Address operators:

The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x [in the expression of &x] may also be a (possibly parenthesized) composite literal.

See related questions:

How to get the pointer of return value from function call?

How can I store reference to the result of an operation in Go?

Possible "workarounds"

"Easiest" (requiring the least change) is simply to assign to a variable, and call the method after that. This is your first working solution.

Another way is to modify the methods to have a value receiver (instead of pointer receiver), so that there is no need to take the address of the return values of the methods, so calls can be "chained". Note that this might not be viable if a method needs to modify the receiver, as that is only possible if it is a pointer (as the receiver is passed just like any other parameters – by making a copy –, and if it's not a pointer, you could only modify the copy).

Another way is to modify the return values to return pointers (*Vector3) instead of Vector3. If the return value is already a pointer, no need to take its address as it's good as-is for the receiver to a method that requires a pointer receiver.

You may also create a simple helper function which returns its address. It could look something like this:

func pv(v Vector3) *Vector3 {     return &v } 

Using it:

dir := pv(projected.Minus(c.Origin)).Normalize() 

This could also be a method of Vector3, e.g.:

func (v Vector3) pv() *Vector3 {     return &v } 

And then using it:

dir := projected.Minus(c.Origin).pv().Normalize() 

Some notes:

If your type consists of 3 float64 values only, you should not see significant performance differences. But you should be consistent about your receiver and result types. If most of your methods have pointer receivers, so should all of them. If most of your methods return pointers, so should all of them.

like image 72
icza Avatar answered Sep 22 '22 17:09

icza


The accepted answer is really long so I'm just going to post what helped me:

I got this error regarding this line:

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

so I just changed it to:

(&services.HashingServices{}).Hash("blabla") 
like image 45
VitalyGo877 Avatar answered Sep 18 '22 17:09

VitalyGo877