Reputation: 109
I am newbie in computer science.I just read the code snippet below from The Go programming language and got the following error log.
func (p *Point) ScaleBy(factor float64){
p.X *= 2
p.Y *= 2
}
Point{1, 2}.ScaleBy(2)
# error log
cannot call pointer method on point literal
cannot take the address of point literal
point literal.scaleby(2) used as value
The book explained that we can not call a *Point
method on a non-addressable Point receiver, because there's no way to obtain address of a temporary value.
However, if I print &Point{1, 2}
, this would not throw error. Accordingly, why Point{1,2}
is a non-addressable Point receiver?
Upvotes: 2
Views: 551
Reputation:
By using Point{1, 2}.ScaleBy(2)
you are trying to call pointer receiver method ScaleBy
with value: Point{1, 2}
:
The method set of any other type T consists of all methods declared with receiver type T.
but if you use addressable type:
The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).
then it is possible: meaning you or the compiler should get the address of temporary value (Taking the address of a composite literal):
Address operators:
For an operand x of type T, the address operation &x generates a pointer of type *T to x. 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 may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.
ref: https://golang.org/ref/spec#Address_operators
You may call (&Point{1, 2}).ScaleBy(2)
like this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := (&Point{1, 2}).ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return p
}
you may call Point{1, 2}.ScaleBy(2)
like this working sample code (value receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}.ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return &p
}
output:
&{2 4}
also see this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}
p.ScaleBy(2)
fmt.Println(p) // {2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) {
p.X *= 2
p.Y *= 2
}
output:
{2 4}
Upvotes: 2
Reputation: 9116
When you write Point{1,2}
you simply declaring and initializing a value of type Point
. If you don't assign it to a variable, it is discarded.
Go disallows this behavior of calling a pointer method on a simple value since a pointer method states an intent of object (pointed to by the pointer) modification. A pointer method called with a value would be useless in most of the cases since a value is passed by copy to the method. Any modifications made to the value will be done to that copied value and no actual modification would occur.
If you tried this, it would work:
type Point struct {
x, y int
}
func (p Point) X() {
fmt.Println(p.x)
}
Point{1, 2}.X() // 1
You can read more about it here: https://golang.org/doc/effective_go.html#pointers_vs_values
Upvotes: 1