Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does inline instantiation of variable requires explicitly taking the address of it to call pointer method, while for a existing var its implict

Tags:

go

Is there a reason for this behaviour? I would like to know what is different in the memory level. The compiler returns "cannot take the address of composite literal" while i can explicitly ask it to do it.

Heres the go playground go playground link

u := User{"john"}
fmt.Println(u.Name()) //implicit

//fmt.Println(User{"john"}.Name()) //Error: cannot call pointer method on composite literal, cannot take the address of composite literal
fmt.Println((&User{"jim"}).Name()) //explicit

type User struct {
    name string
}

func (u *User) Name() string {
    return u.name
}
like image 984
Gnani Avatar asked Sep 01 '14 08:09

Gnani


2 Answers

Because a composite litteral is not addressable until it's been assigned to a variable:

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.

You can take the address to User{"jim"}: (&User{"jim"}) (literal pointer).
But you cannot directly use the value of User{"jim"} (literal value, not until it is assign to a variable).
See more in this thread.


This thread adds to the"Expression isn't addressable" issue:

Taking the address of something, that up to this hypothetical point was not addressable (an expression that doesn't happen to be rooted to an identifier), is semantically no different then setting the dereferenced value of the destination pointer.
In other words, the following are equivalent:

var x *int
*x = (2 * 3) + 4

var x *int
x = &((2 * 3) + 4)

Since an expression is thrown away as soon as it's used, the address of its value (which often is entirely register-resident, and thus doesn't actually have an address) is entirely meaningless in any case, and the only benefit from taking it's address is the syntactic convenience of type-inference in variable declarations (saving at most a single line):

x := &((2 * 3) + 4)

A problem with making arbitrary expressions addressable:

m := map[int]int{1:2}
x := &m[1]
x = 3
fmt.Println("x is", x, "while m[1] is", m[1])
// will print `x is 3 while m[1] is 2`

In short: it'll be unclear (and nigh impossible to debug) whether you're taking the address of something you expect to be addressable, or when you're taking the address of an implicit intermediate var (which is very often what you don't want).

like image 152
VonC Avatar answered Nov 07 '22 18:11

VonC


Is there a reason for this behaviour?

The reason for the behavior is that there is an exception to addressability for that specific notation and the reason for that exception is explained here by one of the Go maintainers :

They're special because we decided that the notation was useful enough for constructors to merit a special case:

Taking the address of a composite literal (§Address operators) 
generates a unique pointer to an instance of the literal's value. 

http://golang.org/doc/go_spec.html#Composite_literals

Russ


"Taking the address of a composite literal (§Address operators) generates a unique pointer to an instance of the literal's value." Could you elaborate that?

I think that means the notation &Object{} notation is simply a literal pointer each time it is executed it creates a new instance of the value and a new pointer is created that points to the new value. The addressability exception simply results from the notation of this literal, it is unlike the other addressables because it is not being acted upon by the & operator.

I would like an answer similar to jesse's on the "here" link you mentioned. That is for C. Can anyone confirm that for golang?

My guess is this that this notation is helpful with escape analysis, which determines at compile time whether a references escapes the function and determines whether it should be allocated on the heap or the stack ( faster ). From what I understand the variables that have their address taken are considered candidates for escape analysis. Since one of Go's main goals is fast compilation I think reference creation and tracking is required to be clear.

For example, for Item{}.Method() the need to create a pointer and therefore run escape analysis to the Item{} value would depend on the receiver type of the Method() whether it is *Item or just Item this unnecessarily complicates compilation. That's why you use the explicit notation &Item{} to construct a *Item.

like image 22
Emil Davtyan Avatar answered Nov 07 '22 18:11

Emil Davtyan