Why can't you take the address of a function's return value?

4,198 views
Skip to first unread message

con...@superhuman.com

unread,
Aug 22, 2016, 5:25:03 PM8/22/16
to golang-nuts
Hey All,

I've been using a typedef of

    type MaybeTimestamp *int64

so we can JSON encode timestamps correctly, see https://medium.com/coding-and-deploying-in-the-cloud/time-stamps-in-golang-abcaf581b72f for inspriation

I was surprised that the following didn't work (see https://play.golang.org/p/1QQylqTLkB):

    func NewMaybeTimestamp(t time.Time) MaybeTimestamp {
        return &t.Unix()
    }

    // fails with:
    // tmp/sandbox449672725/main.go:14: cannot take the address of t.Unix()

I can fix this by introducing a temporary variable:

    func NewMaybeTimestamp(t time.Time) MaybeTimestamp {
        temp := t.Unix()
        return &temp
    }

Seeing as this is the obvious solution to this problem, I was pretty disappointed that the compiler couldn't insert the temporary for me.

Is this something that should change, or is it just a limitation hard-coded by the compatibility guarantee?

Conrad

Ian Lance Taylor

unread,
Aug 22, 2016, 6:09:44 PM8/22/16
to con...@superhuman.com, golang-nuts
There is no compatibility guarantee issue here, as adding this feature
would not break any existing working programs.

However, in my opinion, it should not be added. In general, the &
operator takes the address of some existing variable. There is one
exception: using &T{} to create a composite literal and takes its
address. You are suggesting adding another exception &f() should call
the function, store the value somewhere in memory, and return the
address of that memory. This is meaningful and I don't see any reason
why it wouldn't work. But it's cryptic, and it's rarely useful. I
think it's OK to use a variable.

Ian

Jan Mercl

unread,
Aug 22, 2016, 6:10:27 PM8/22/16
to con...@superhuman.com, golang-nuts
Strictly speaking you can take the address of function's return value (eg. https://play.golang.org/p/0PTrkWEirW). It's like taking the address of any variable. But that's an lvalue, which &f() is not.

--

-j

Peter Waller

unread,
Aug 22, 2016, 7:40:59 PM8/22/16
to Jan Mercl, con...@superhuman.com, golang-nuts
On 22 August 2016 at 19:09, Jan Mercl <0xj...@gmail.com> wrote:

Strictly speaking you can take the address of function's return value (eg. https://play.golang.org/p/0PTrkWEirW). It's like taking the address of any variable. But that's an lvalue, which &f() is not.
 
You just exploded my head and made me laugh. Thanks for that.

T L

unread,
Aug 27, 2016, 3:34:05 AM8/27/16
to golang-nuts, con...@superhuman.com

Direct values, including const and temp values, are not addressable. But there is an exception which is described in Ian's answer.
 

Conrad

T L

unread,
Aug 27, 2016, 3:58:53 AM8/27/16
to golang-nuts, con...@superhuman.com

The counter rule is: variables, including elements of variables, are always addressable.
And there is also an exception for the counter rule: map elements are not addressable.

Please adapt it. Almost for every rule in Golang, there are some exceptions.

 

Conrad

Florin Pățan

unread,
Aug 28, 2016, 2:07:49 AM8/28/16
to golang-nuts
This request, yo take the address of a return value of a function, seems to conflict with the last request made here to standardize and not have exceptions to rules.

And I say this because in Go a function can have multiple return values, most commonly a (type, error) pair.

How should that work then? Add an exception so that single value return functions can be used like this? Add an exception so that when a couple of values are returned then if one of them is error it doesn't take the address for that? What would happen in case of more than two return arguments?

T L

unread,
Aug 28, 2016, 3:02:04 AM8/28/16
to golang-nuts

The exception is ready added: one return result can be used in expressions.

adon...@google.com

unread,
Aug 29, 2016, 5:17:56 PM8/29/16
to golang-nuts, con...@superhuman.com
On Friday, 26 August 2016 23:58:53 UTC-4, T L wrote:
And there is also an exception for the counter rule: map elements are not addressable.

Just because you can use the assignment syntax m[k]=v to update a map element does not mean a map element is a variable ("addressable").  This is not an exception to a rule.
 
Please adapt it.

Compatibility with Go 1.0 means that fundamental changes to the semantics, no matter how sensible or popular, are not going to happen.

Conrad Irwin

unread,
Aug 29, 2016, 5:36:00 PM8/29/16
to adon...@google.com, golang-nuts
I guess the thing that is disconcerting to me is that because of the automatic escape detection, I no longer think of a pointer as being the intrinsic address of a value; rather in my mind the & operator creates a new pointer value that when dereferenced returns the value.

With that mental model mixup in place, it's obvious why "&f()" makes sense — it's just creating a new pointer to the value returned by "f()".

If you instead keep in mind that the meaning of "&" is supposed to be closer to "what's the address of this thing?" for the purpose of identity-based equality and reference sharing, it makes more sense to prohibit "&m[k]" or "&f()" because each time you run those you may/will get a new pointer (which is not useful for identity-based equality or reference sharing). It still would be useful for my case which was essentially converting one type to an "optional" type, but maybe that's enough of an edge case that it doesn't matter.

Conrad

Sent via Superhuman

adon...@google.com

unread,
Aug 29, 2016, 5:56:32 PM8/29/16
to golang-nuts, adon...@google.com, con...@superhuman.com
On Monday, 29 August 2016 13:36:00 UTC-4, Conrad Irwin wrote:
because of the automatic escape detection, I no longer think of a pointer as being the intrinsic address of a value; rather in my mind the & operator creates a new pointer value that when dereferenced returns the value.
 
With that mental model mixup in place, it's obvious why "&f()" makes sense — it's just creating a new pointer to the value returned by "f()".
 
If you instead keep in mind that the meaning of "&" is supposed to be closer to "what's the address of this thing?" for the purpose of identity-based equality and reference sharing, it makes more sense to prohibit "&m[k]" or "&f()" because each time you run those you may/will get a new pointer (which is not useful for identity-based equality or reference sharing). It still would be useful for my case which was essentially converting one type to an "optional" type, but maybe that's enough of an edge case that it doesn't matter.

The composite literal syntax &T{...} may have led you astray, as it is not equivalent to "&" + "T{}".  That is, it doesn't mean "here's an expression of type T, give me its address", it means, "create a new variable of type T, initialize it, then give me its address".  It's shorthand for:

   p := new(T); *p = T{...}; p

The key thing is that only variables have addresses.  (Escape analysis is irrelevant: all of this applies equally to stack and heap variables.)
Reply all
Reply to author
Forward
0 new messages