-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
proposal: Go 2: allow taking the address of a function result value or constant #22647
Comments
Ah, thanks @dsnet, I'd missed that (I searched, but I searched for Despite the fact that I've looked at #9097 but don't really go for the I'd also like to point out two instances of the |
The easiest way to write that Amazon API would have been to not have pointers ruthlessly pointing to everything (including immutable types). Is the need indicated anywhere outside those examples already mentioned in the proposal? |
@as But as you can see from the AWS issue thread, they did it like that for a reason -- because they need to distinguish between zero value (say 0 or empty string) and an unset value:
Similar with the Go protobuf library -- it has helper functions for all the basic types to help with setting optional fields. So there are two major libraries that have this issue, plus the two instances in the stdlib tests, plus two popular StackOverflow questions and two golang-nuts threads that I linked to, as well as my own experience. In that light, it seems a bit dismissive to ask "is the need indicated anywhere" outside the 9 examples and 2 existing issues... |
My apologies. It was not intended to be dismissive, the purpose was inquisition and it served to expand the discussion.
In my opinion, it's important to see whether they need to do it or just want to do it. The thread shows some concern about how an Amazon service might change it's own semantics in the future, breaking the SDK if |
Another related use-case is Create().Use(), where Create returns a value and Use has a pointer receiver. You can’t do that in Go and are forced to use a temporary, but it doesn’t appear to be a good reason for this limitation. |
@as Thanks. And what you say below is a fair point:
I can definitely see the need to distinguish between zero value and unset value in some cases, but with a quick glance at the actual Still, such use cases are reasonable -- I think they probably didn't choose to do it this way lightly, given the size and need for consistency in their API. It'd be equally awkward having most parameters be values and some (maybe In any case, I think the more important use cases would probably be where I ran into it, for things like nullable database fields. If Go had option types (and I know there are proposals for that as well) that would solve this situation too -- you don't really want a pointer, you just want to denote the fact that a field can be set or unset. |
It's also an interesting question in general: if we permit |
@ianlancetaylor Using A situation when |
I don't think that's true in general. For instance, the above syntax is very common for logging libraries where the intent of the user is printing a line; the fact that the object is modified or not in the process is an implementation detail of the library itself. Obviously, the libraries can still be written today, using either always a pointer or always a value (for both Create and Use), but I still can't see why a mix should be forbidden (and just for the case of a temporary, while allowed for an explicit temporary). There's also a performance argument. Go's escape analysis is far from perfect, and elision of useless receiver copies is non existent; every method call with a value receiver generates a copy of the receiver, irrespective of the fact that the copy is actually required or not. In fact, if the method does not modify the value, the copy is useless most of the time. I tend to prefer pointer receivers for this reason unless the value is really simple (eg: a basic type). |
Those statements are implementation details not covered by the compatibility promise (unlike the proposed change would be), even if they are correct (and without benchmarks that is impossible to know) |
At the moment, pointer receivers conflate two otherwise-orthogonal properties: efficiency (avoiding unnecessary copies) and identity (ensuring that writes do or do not go to the underlying object). If In the wild, this comes up in the form of the Builder pattern: if you want to write a Builder-like API in Go but pass pointers for efficiency reasons, you must wrap those pointers in an extra |
That may be true, but as abstractions they're very leaky. Fundamentally, escape analysis and copy elision both rely on some form of alias analysis, which is difficult to do precisely — especially in a language as dynamic as Go. If you want to elide a copy of a function argument, you need to know not only that the callee doesn't modify the argument directly, but also that it doesn't modify the argument indirectly (such as through a passed-in function or interface argument). You could argue that a sufficiently clever compiler should be able to do that analysis anyway, but the Go compiler seems unlikely to be that sufficiently clever any time soon. So the viable options seem to be either to accept that pointer-receivers-as-optimization will sometimes make call sites more awkward, or accept that pointer-receivers-as-identity will allow aliasing bugs to occur. It seems to me that the use of garbage collection already biases Go toward cleaner call sites at the expense of aliasing bugs, so I think making function arguments addressable would be fairly consistent with the language as it stands. (But I freely admit that that's entirely subjective.) |
#9097 proposes a more general syntax that works for all kinds of expressions, not just function calls and constants. Closing this issue in favor of that one. We can discuss syntax details over there. |
@ianlancetaylor Hmm, I'm obviously biased, but I think this issue is better than #9097, for a few reasons:
Don't get me wrong, I think the |
Yes, please discuss over at #9097. The two issues are basically the same idea with different syntax, so I'm consolidating the discussion in one place, the earlier issue. |
Will do, thanks. |
As I noted in http://benhoyt.com/writings/learning-go/#general-quirks, one of the (few) language quirks I noticed when learning Go is that I couldn't take the address of a function result. For example, here's what I wrote at first and thought should work:
But of course it didn't because, according to the spec, the thing you're taking the address of needs to be "addressable" or, as a special case, a composite literal like
&Point{2, 3}
.It seems like this would be a backwards-compatible change to the Go language spec to allow this, and have the compiler figure out that it needs to create a temporary variable (on the heap or stack) to take the address of. The compiler could expand it to the equivalent of:
And it seems I'm not the only one who has run into this in slightly different ways -- see one, two, three, and four.
In particular, the Amazon AWS SDK has a whole bunch of helper functions like aws.String (docs here), just so you can use constants as "optional parameters" for pointer fields in input structs, eg:
Before they added this (see here), you'd have to do this in a very unwieldy way:
With this proposal in place, not only could the AWS SDK get rid of all those
aws.String
-style helpers, but there'd be an obvious, clean way to write this:Obviously there are a couple of things to think about:
&(x + 1234)
. I think it'd be nice and orthogonal if it allows arbitrary expressions, but if there's a good reason to be conservative, I think just allowing&functionCall()
and&constant
would be a great start.&Point{2, 3}
.The text was updated successfully, but these errors were encountered: