allow &int{3}, &bool{true} etc

442 views
Skip to first unread message

Nate Finch

unread,
Oct 21, 2016, 12:49:24 PM10/21/16
to golang-nuts
One of the problems with working with serialization of any kind is dealing with unset vs the zero value.  The usual way to get around this is to use a pointer, nil is unset, non-nil is set.  But this gets annoying when trying to make literal values, because you can't take the address of a constant:

type Foo struct {
    Count *int
}
f := Foo{ Count: &5 }

// cannot take the address of 5

I get that you can't take the address of a constant, because then you could change it, even though it's constant... but the alternatives are really clunky.   Sure, you can make a helper function for it, but that just makes your API significantly less user-friendly, either you need ugly, basically useless functions in your API, or you force your consumers to make those functions in every package that consumes yours.  There's a ton of hacks you can do to make it work without implementing functions, but they're each more horrible than the last: https://play.golang.org/p/9r-JMFSx9C

I'd really like to see the composite literal syntax extended to work with non-composite types, so you could do something like this:

f := Foo{ Count: &int{5} }

This would explicitly tell you that you're making a copy of the constant 5 and then taking the address of that copy, just like you would here:

type Bar struct {
    Val int
}
b := &Bar{5}

It seems like it would be intuitive and backwards compatible.

Alternatively, I actually think it would be totally fine and intuitive to just let you do &5, but I bet that has less likelihood of being accepted ;)

-Nate

Konstantin Khomoutov

unread,
Oct 21, 2016, 12:56:05 PM10/21/16
to Nate Finch, golang-nuts
On Fri, 21 Oct 2016 09:49:24 -0700 (PDT)
Nate Finch <nate....@gmail.com> wrote:

> One of the problems with working with serialization of any kind is
> dealing with unset vs the zero value. The usual way to get around
> this is to use a pointer, nil is unset, non-nil is set. But this
> gets annoying when trying to make literal values, because you can't
> take the address of a constant:
>
> type Foo struct {
> Count *int
> }
> f := Foo{ Count: &5 }
>
> // cannot take the address of 5

See also the recent thread (14 Oct) on the same topic:
https://groups.google.com/d/topic/golang-nuts/NR_oWflin-Q/discussion

[...]

Nate Finch

unread,
Oct 21, 2016, 1:06:36 PM10/21/16
to golang-nuts, nate....@gmail.com
I had missed that, thanks.  The one response does not really fit the problem, though.  There are a lot of times when POS (plain old structs) make a really nice UX (or would, if they weren't a pain to handle).  I like option types like Dave said, but that's really a whole different beast that solves different problems.

Caleb Spare

unread,
Oct 21, 2016, 1:16:13 PM10/21/16
to Nate Finch, golang-nuts
I would also like an easier way to construct pointers to ints/bools/strings.

Note that there is a way to write convoluted code that makes e.g.
&int{3} legal: https://play.golang.org/p/leP5_12IX0
AFAICT this isn't a backwards-compatibility concern, though.

Like you, I would really like &5 to work but I haven't thought through
the kinds of spec changes that would be required to make that possible.

-Caleb
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Ian Lance Taylor

unread,
Oct 21, 2016, 1:23:04 PM10/21/16
to Nate Finch, golang-nuts
On Fri, Oct 21, 2016 at 9:49 AM, Nate Finch <nate....@gmail.com> wrote:
>
> I'd really like to see the composite literal syntax extended to work with
> non-composite types, so you could do something like this:
>
> f := Foo{ Count: &int{5} }

That doesn't work as is, because &[]interface{nil} is ambiguous. Does
it produce a slice whose length is 1 element and whose single value is
nil (as it does today), or does it produce a nil slice whose length is
zero elements (as it would if we treat &T{v} as allocating a new T and
initializing it to V).

(This overall issue has been discussed many times before, with no
clear resolution.)

Ian

xingtao zhao

unread,
Oct 21, 2016, 1:50:21 PM10/21/16
to golang-nuts, nate....@gmail.com
If we adopt the syntax like &int(0), there will be no ambiguities, even for the example Ian showed: the two cases will be written as &[]interface{}(nil) and &[]interface{}{nil}
you may argue that this is confusing, but both []interface{}(nil) and []interface{}{nil} are available already.

Nate Finch

unread,
Oct 21, 2016, 2:39:23 PM10/21/16
to golang-nuts, nate....@gmail.com
I don't think it's ambiguous at all.  We already do different things for different T in T{v}.  For slices, the slice is constructed and then each value is an item in the slice.  For structs, the struct is constructed and then the fields are filled out in declaration order.  We're just adding one more rule for single values which is actually really really obvious.

In fact, if anything, we're making it more consistent....

&[]int{5}  // this works
&[1]int{5} // this doesn't
&int{5} // this doesn't

Why not make those last two just work?

As xingtao zhao said, &[]interface{}{nil} is not ambiguous.  it's declaring a slice, so it fills in one item in the slice and returns the address of the slice.

Ian Lance Taylor

unread,
Oct 21, 2016, 2:53:31 PM10/21/16
to Nate Finch, golang-nuts
You are simply defining away the ambiguity by saying that the specific
slice composite literal syntax takes precedence over the &T{v}
translates to (*new(T) = v) syntax. That is a valid approach to take,
but it doesn't mean that there is no ambiguity. And it does mean
additional complexity to the spec and to people's understanding of the
language.

Ian

Nate Finch

unread,
Oct 21, 2016, 3:19:59 PM10/21/16
to golang-nuts, nate....@gmail.com
That's fair.  But I think it's a worthwhile (and fairly minor) complexity to add to the spec to make the language a lot easier to use in some very common use cases.  And, I would argue, it actually makes the language slightly more regular, since now &T{v} works for just about everything (possibly everything?).  

I'd love to see a bigquery search for variants of declarations of func IntP(i int) *int  ... I bet there's a lot of those out there... unless people are really doing &[]int{5}[0] ... which is just horrible.

Jan Mercl

unread,
Oct 21, 2016, 3:59:48 PM10/21/16
to Nate Finch, golang-nuts
On Fri, Oct 21, 2016 at 9:20 PM Nate Finch <nate....@gmail.com> wrote:

> And, I would argue, it actually makes the language slightly more regular, since now &T{v} works for just about everything (possibly everything?). 

Taking the address of an addressable thing is the regular proper. Taking address of a non-addressable things, even though practical, is syntactic sugar. Enlarging the surface of the later irregularity cannot make anything more regular.

--

-j

Nate Finch

unread,
Oct 21, 2016, 4:44:37 PM10/21/16
to Jan Mercl, golang-nuts
Perhaps regular was the wrong choice of phrasing.  From an end-user's perspective, it makes the language more consistent, rather than having &T{v} work for some of the more complex values of T, but not for the more simple values of T.

Matt Harden

unread,
Oct 22, 2016, 11:43:39 AM10/22/16
to Nate Finch, Jan Mercl, golang-nuts
I don't like the syntax &int{0} because int is not a compound type, but &int(0) seems reasonable syntax to me. I do think there is an irregularity in the language here:

type Int struct{i int}
anInt := Int{5}
p1 := &Int{5}     // works
p2 := &anInt.i    // works
p3 := &Int{5}.i   // compiler error "cannot take the address of Int literal.i"

So we can take the address of an Int literal (suggesting that struct literals are addressable), and we can take the address of a field of an addressable struct, but we can't take the address of a field of a struct literal?

--

Ian Lance Taylor

unread,
Oct 22, 2016, 12:19:24 PM10/22/16
to Matt Harden, Nate Finch, Jan Mercl, golang-nuts
On Sat, Oct 22, 2016 at 8:42 AM, Matt Harden <matt....@gmail.com> wrote:
> I don't like the syntax &int{0} because int is not a compound type, but
> &int(0) seems reasonable syntax to me. I do think there is an irregularity
> in the language here:
>
> type Int struct{i int}
> anInt := Int{5}
> p1 := &Int{5} // works
> p2 := &anInt.i // works
> p3 := &Int{5}.i // compiler error "cannot take the address of Int
> literal.i"
>
> So we can take the address of an Int literal (suggesting that struct
> literals are addressable), and we can take the address of a field of an
> addressable struct, but we can't take the address of a field of a struct
> literal?

That is correct. Struct literals (and composite literals in general)
are not addressable. The &T{v1, v2} syntax is a special syntactic
sugar meaning to allocate a new instance of T, initialize elements,
and return a pointer to the result. At one point we considered
changing it to (*T){v1, v2} to emphasize that this is really an
expression that returns a value of type *T, but we decided that &T{v1,
v2} looked better and was clear enough.

You can, of course, write
p3 := &(&Int{5}).i

Ian

Nate Finch

unread,
Oct 22, 2016, 12:29:50 PM10/22/16
to Ian Lance Taylor, Matt Harden, Jan Mercl, golang-nuts
Which is effectively the same as my proposal, except horrible. 

Matt Harden

unread,
Oct 22, 2016, 2:07:35 PM10/22/16
to Nate Finch, Ian Lance Taylor, Jan Mercl, golang-nuts
&(&struct{int}{5}).int also works. :-)

Matt Harden

unread,
Oct 22, 2016, 2:20:03 PM10/22/16
to Nate Finch, Ian Lance Taylor, Jan Mercl, golang-nuts
Interesting - &[]int{5}[0] works, but &[...]int{5}[0] doesn't, and [...]int{5}[:] is also illegal (slice of unaddressable value). I guess I always thought of []T{x,y,z} as sugar for [...]T{x,y,z}[:] but it's more like func()[]T{a := [...]T{x,y,z}; return a[:]}()

I agree these are horrible. I just enjoy exploring the dark corners.

Pietro Gagliardi

unread,
Oct 22, 2016, 2:31:58 PM10/22/16
to Matt Harden, Nate Finch, Ian Lance Taylor, Jan Mercl, golang-nuts

On Oct 22, 2016, at 2:19 PM, Matt Harden <matt....@gmail.com> wrote:

and [...]int{5}[:] is also illegal (slice of unaddressable value)

[]int{5} will do the same thing, and I didn't know this until recently but the spec is written such that this even works with named indices:

v := []int{
5: 5,
}
fmt.Println(len(v)) // prints 6

tylerb...@gmail.com

unread,
Oct 22, 2016, 2:58:32 PM10/22/16
to golang-nuts, matt....@gmail.com, nate....@gmail.com, ia...@golang.org, 0xj...@gmail.com
I agree that a spoonful of syntactic sugar would be wonderful here, though I don't have any strong opinions on what form it should take.

roger peppe

unread,
Oct 22, 2016, 6:18:22 PM10/22/16
to Nate Finch, golang-nuts, Jan Mercl

When I need to do this, I find it's only a very minor annoyance to define:

    func newInt(i int) { return &i }

If Go ever got generics, this would probably be trivial to write generically, for example:

    func Ref[T](x T) *T { return &x }

I don't think I'd object if we added a new builtin function called "ref" with the above semantics. It worked pretty well in Limbo as an operator.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Nate Finch

unread,
Oct 22, 2016, 7:50:37 PM10/22/16
to roger peppe, golang-nuts, Jan Mercl

I'd much rather have syntax that just works rather than another built-in function.


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

roger peppe

unread,
Oct 24, 2016, 3:45:29 PM10/24/16
to Nate Finch, Jan Mercl, golang-nuts

How would new syntax be better than a built-in function with exactly the semantics you are after and shorter to type to boot?


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Nate Finch

unread,
Oct 24, 2016, 4:14:47 PM10/24/16
to golang-nuts, nate....@gmail.com, 0xj...@gmail.com
Because it's consistent with how you get a pointer for other types... if I'm used to using &T{x} for structs, maps, and slices, then it's perfectly natural to try that with builtins and arrays.  If someone says "oh, sorry, for ints you have to use ref(5)" then I'm going to get annoyed at an inconsistency that seems to exist for no reason.

It's also less likely to overlap with existing code.  A function called ref could very well already exist in a current codebase.  But there's likely almost no code that uses builtin type names as anything other than the types themselves.  And unless you have structs that use builtin type names, &int{5} won't currently compile.

Henrik Johansson

unread,
Oct 25, 2016, 12:49:46 AM10/25/16
to Nate Finch, golang-nuts, 0xj...@gmail.com

I agree with Nate. The consistency in typing is a very compelling argument.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

roger peppe

unread,
Oct 25, 2016, 6:51:05 AM10/25/16
to Nate Finch, golang-nuts, Jan Mercl
On 24 October 2016 at 21:14, Nate Finch <nate....@gmail.com> wrote:
> Because it's consistent with how you get a pointer for other types... if I'm
> used to using &T{x} for structs, maps, and slices, then it's perfectly
> natural to try that with builtins and arrays. If someone says "oh, sorry,
> for ints you have to use ref(5)" then I'm going to get annoyed at an
> inconsistency that seems to exist for no reason.
>
> It's also less likely to overlap with existing code. A function called ref
> could very well already exist in a current codebase. But there's likely
> almost no code that uses builtin type names as anything other than the types
> themselves. And unless you have structs that use builtin type names,
> &int{5} won't currently compile.

It doesn't really matter if a function called ref is defined already,
as it won't be a problem until you actually want to use it.
(FWIW there are no functions named "ref" in the 1640556
lines of Go code in my GOPATH).

To me the T{...} syntax says "structured type literal" - wrapping
an integer in {} and saying that it's a scalar value seems wrong.

I like "ref" because it involves almost no changes to existing
tooling and it's also useful in other case.

For example, I commonly want to make a shallow copy
of an object. Currently I tend to do this:

func x(t *T) {
t1 := *t
y(&t1)
}

but feels a little awkward that t1 is of a different type than T.

func x(t *T) {
t1 := ref(*t)
y(t1)
}

feels a little cleaner.

cheers,
rog.


>
> On Monday, October 24, 2016 at 3:45:29 PM UTC-4, rog wrote:
>>
>> How would new syntax be better than a built-in function with exactly the
>> semantics you are after and shorter to type to boot?
>>
>>
>> On 23 Oct 2016 01:50, "Nate Finch" <nate....@gmail.com> wrote:
>>>
>>> I'd much rather have syntax that just works rather than another built-in
>>> function.
>>>
>>>
>>> On Sat, Oct 22, 2016, 6:17 PM roger peppe <rogp...@gmail.com> wrote:
>>>>
>>>> I don't think I'd object if we added a new builtin function called "ref"
>>>> with the above semantics. It worked pretty well in Limbo as an operator.
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages