Removing "new"

693 views
Skip to first unread message

Fumin Wang

unread,
Sep 11, 2013, 11:29:57 AM9/11/13
to golan...@googlegroups.com
After getting pretty confused by "new", I came across this 2010 thread containing a proposal by Rob which made a lot of sense to me.

https://groups.google.com/forum/#!topic/golang-nuts/kWXYU95XN04%5B1-25-false%5D

Any reasons why not go for this proposal?

As a new comer to Golang for less than a month, +1 for this proposal in getting rid of new. As an experienced practitioner in C, C++, Java, and Ruby (the last three of them have `new`s albeit with different meanings), I appreciate a lot of Go's creators amount of thinking that's put into the semantics and syntax of the language. However, `new` is an exception to such elegant and comprehensive thought, as it not only creates confusion for people that come from other languages that have "new", but also is redundant and can be replaced by either "&T{}" or the proposed "make(*T)". To me, these two alternatives are even superior than "new" as they resonates with what I know of "structs" and "pointers" in C.

p.s. I wanted to post a reply to that 2010 post, but google groups kept returning an error. Please forgive me if I broke the rule for posting a new post in reply of an old post.

Ian Lance Taylor

unread,
Sep 11, 2013, 11:48:19 AM9/11/13
to Fumin Wang, golang-nuts
On Wed, Sep 11, 2013 at 8:29 AM, Fumin Wang <awaw...@gmail.com> wrote:
> After getting pretty confused by "new", I came across this 2010 thread
> containing a proposal by Rob which made a lot of sense to me.
>
> https://groups.google.com/forum/#!topic/golang-nuts/kWXYU95XN04%5B1-25-false%5D
>
> Any reasons why not go for this proposal?

I think there was no clear consensus that that proposal was better
than the current situation.

Ian

Jan Mercl

unread,
Sep 11, 2013, 12:02:32 PM9/11/13
to Fumin Wang, golang-nuts
On Wed, Sep 11, 2013 at 5:29 PM, Fumin Wang <awaw...@gmail.com> wrote:
> However, `new` is an exception to such
> elegant and comprehensive thought, as it not only creates confusion for
> people that come from other languages that have "new",

Not true, IMO. Pascal has 'new' and people coming from Pascal can
hardly be confused as it works exactly the same in Pascal and Go.

> but also is redundant
> and can be replaced by either "&T{}"

Not true, IMO. "&int{}" doesn't work.

> or the proposed "make(*T)".

Proposed three years ago and never adopted. Have something changed in
those three years?

-j

PS: I see zero confusion in "new". Yes, I came from Pascal ;-)

paulo....@gmail.com

unread,
Sep 11, 2013, 12:19:04 PM9/11/13
to golan...@googlegroups.com, Fumin Wang
Pascal family of languages don't have make. Only new exists.

Fumin Wang

unread,
Sep 11, 2013, 12:25:58 PM9/11/13
to golan...@googlegroups.com, Fumin Wang

On Thursday, 12 September 2013 00:02:32 UTC+8, Jan Mercl wrote:
On Wed, Sep 11, 2013 at 5:29 PM, Fumin Wang <awaw...@gmail.com> wrote:
> However, `new` is an exception to such
> elegant and comprehensive thought, as it not only creates confusion for
> people that come from other languages that have "new",

Not true, IMO. Pascal has 'new' and people coming from Pascal can
hardly be confused as it works exactly the same in Pascal and Go.
Jan, thanks for your feedback on Pascal, great to learn something new today. :)

> but also is redundant
> and can be replaced by either "&T{}"

Not true, IMO. "&int{}" doesn't work.
Right, I saw this argument in the 2010 post, but I doubt this would be a problem to anyone who wants to learn Go seriously which puts emphasis on programmers' basic understanding of "pointers" and the relation between memory management and efficiency.

Jan Mercl

unread,
Sep 11, 2013, 12:41:55 PM9/11/13
to Fumin Wang, golang-nuts
On Wed, Sep 11, 2013 at 6:25 PM, Fumin Wang <awaw...@gmail.com> wrote:
> Right, I saw this argument in the 2010 post, but I doubt this would be a
> problem to anyone who wants to learn Go seriously which puts emphasis on
> programmers' basic understanding of "pointers" and the relation between
> memory management and efficiency.

Well, Go: 'new(T)' is just C: 'calloc(sizeof(T));'. Doesn't seem
complicated for someone wanting to understand the concepts you're
mentioning.

-j

Fumin Wang

unread,
Sep 11, 2013, 12:54:49 PM9/11/13
to golan...@googlegroups.com, Fumin Wang
Well, the confusing part is that after explaining that Go's struct are similar to C's in http://tour.golang.org/#28 , the next page http://tour.golang.org/#29 introduces "new" which seems redundant in light of "&T{}". Additional keywords or syntax in a language usually serve a special purpose as in C++ and Java and Ruby, leaving me wondering why this additional "new"?
To me 'calloc(sizeof(T))' is clearer than the natural language description http://tour.golang.org/#29 , but I doubt it's equally clear to others.

-j

wkharold

unread,
Sep 11, 2013, 1:14:34 PM9/11/13
to golan...@googlegroups.com, Fumin Wang
If I were king I would've done make(*T) in the interest of parsimony. That said, this discussion is academic since removing new() would break the Go 1 compatibility guarantee. Ergo, new() is here to stay; learn it, live it, love it.

GreatOdinsRaven

unread,
Sep 11, 2013, 1:17:05 PM9/11/13
to golan...@googlegroups.com, Fumin Wang
To me, there's a fundamental difference between new(T) and &T{}. New allocations some memory and returns a pointer. Done. That's the only reason I use it for - Allocation. Something like &Person{Name: "Joe", Age: 33} I use to both allocate memory and *initialize* said memory to certain values, in one fell swoop. It's like a shortcut = allocate + initialize. New is allocate only. Same with make. 

I can do m := map[string]bool {"Hello": true} which will fully initialize a map with string keys and boo values and exactly *one* key-value pair. However, I can still do this:
make(map[string]bool, 100) which will initialize an *empty* map with initial capacity of 100 key-value pairs. 

So they're fundamentally different cases, to me. I use both. There *is* some (unfortunate) overlap, but I don't think it's a huge deal. 

There's also the fact that new(int) is valid, but &int{} is not. 

Fumin Wang

unread,
Sep 11, 2013, 1:38:20 PM9/11/13
to golan...@googlegroups.com, Fumin Wang
Thanks for everyone's feedback, the background of me running into this academic question is my use of `reply := new(string)` in this line

https://github.com/fumin/rtree/blob/master/rtree_rpc.go#L111 .

My friend or code reviewer questioned my use of "new" in this occasion instead of the plain `var reply string; // and then pass reply by reference &reply` (The requirement of a pointer to something is made by the standard rpc package http://golang.org/pkg/net/rpc/).

This prompted me to dig into "new", and for a while I was confused with whether there is special meaning to "new". Glad it turns out that there aren't any. Nevertheless, now my resolution is to avoid using "new" and instead rely solely on "&T{}" when allowed.

Thanks Harold for reminding of the compatibility guarantee, I'd not pursue this issue any further.

zephyr...@gmail.com

unread,
Sep 11, 2013, 3:53:05 PM9/11/13
to golan...@googlegroups.com
Personally, I find new() to be the most convenient and readable way to work with math/big. z := new(big.Int).Add(x, y) looks much better than z := (&big.Int{}).Add(x, y) and more intuitive than z := big.NewInt(0).Add(x, y).

Rémy Oudompheng

unread,
Sep 11, 2013, 3:56:19 PM9/11/13
to GreatOdinsRaven, golang-nuts, Fumin Wang
On 2013/9/11 GreatOdinsRaven <dimiter....@gmail.com> wrote:
> To me, there's a fundamental difference between new(T) and &T{}. New
> allocations some memory and returns a pointer. Done. That's the only reason
> I use it for - Allocation. Something like &Person{Name: "Joe", Age: 33} I
> use to both allocate memory and *initialize* said memory to certain values,
> in one fell swoop. It's like a shortcut = allocate + initialize. New is
> allocate only. Same with make.
>
> I can do m := map[string]bool {"Hello": true} which will fully initialize a
> map with string keys and boo values and exactly *one* key-value pair.
> However, I can still do this:
> make(map[string]bool, 100) which will initialize an *empty* map with initial
> capacity of 100 key-value pairs.
>
> So they're fundamentally different cases, to me. I use both. There *is* some
> (unfortunate) overlap, but I don't think it's a huge deal.
>
> There's also the fact that new(int) is valid, but &int{} is not.

There is another fundamental difference. new returns a pointer to a
valid Go type. make allocates something which is not representable by
a Go type.

Rémy.

Andrew Gerrand

unread,
Sep 11, 2013, 7:46:18 PM9/11/13
to Fumin Wang, golang-nuts
On 12 September 2013 01:29, Fumin Wang <awaw...@gmail.com> wrote:
Any reasons why not go for this proposal?

We can't make incompatible language changes until Go 2 (years away):

Andrew

Steven Blenkinsop

unread,
Sep 11, 2013, 11:19:07 PM9/11/13
to Rémy Oudompheng, GreatOdinsRaven, golang-nuts, Fumin Wang
On Wed, Sep 11, 2013 at 3:56 PM, Rémy Oudompheng <remyoud...@gmail.com> wrote:

There is another fundamental difference. new returns a pointer to a
valid Go type. make allocates something which is not representable by
a Go type.

Sort of true, but a somewhat arbitrary distinction. Slices are more similar to pointers than they are to maps. I don't think there's any strong argument that either new(T) or make(*T) is fundamentally right or wrong, so it really comes down to a design decision that has already been made. I don't think it's particularly useful to pretend that the particular choice that was made here is the One True Way. In fact, this approach tends to get language communities blacklisted in a lot of people's books if it's too widespread.

Andrew Gerrand

unread,
Sep 12, 2013, 2:29:32 AM9/12/13
to Steven Blenkinsop, Rémy Oudompheng, GreatOdinsRaven, golang-nuts, Fumin Wang
On 12 September 2013 13:19, Steven Blenkinsop <stev...@gmail.com> wrote:
Sort of true, but a somewhat arbitrary distinction.

It's not arbitrary. It's one of the fundamental distinctions between make and new. That's Rémy's point.
 
Slices are more similar to pointers than they are to maps.

That's not true. Map, slice, and channel types have more in common with each other than with pointers. That's why they are all associated with the make function.
 
I don't think there's any strong argument that either new(T) or make(*T) is fundamentally right or wrong, so it really comes down to a design decision that has already been made.

That is very true, and I think most of us agree with that.
 
I don't think it's particularly useful to pretend that the particular choice that was made here is the One True Way. In fact, this approach tends to get language communities blacklisted in a lot of people's books if it's too widespread.

I don't think anyone here is arguing that the status quo is the One True Way, merely that the current design is not arbitrary.

Andrew

Fumin Wang

unread,
Sep 12, 2013, 4:16:39 AM9/12/13
to Andrew Gerrand, Steven Blenkinsop, Rémy Oudompheng, GreatOdinsRaven, golang-nuts
I appreciation everyone's input, just in case someone else stumbles across these question again in the future, here's my summarization:

Q: Is "new" fully replaceable with other syntax in Go?
Ans: Yes, for structs it's completely identically with "&T{}". For other types, for example `string`, we may write:
```
// Assuming the signature of foo is foo(s *string),
// The following are equivalent
s_ptr := new(string)
foo(s_ptr)
// ---------------------------
var s string
foo(&s)
```

Q: If so, why not remove "new"?
Ans: Go's compatible commitment http://golang.org/doc/go1compat.html .

Personally, I'm fully convinced that since the compatible commitment is more important than parsimony in the syntax, therefore "new" should stay. I appreciation such kind of comprehensive thought put into Go a lot, thanks Go devs.

Jan Mercl

unread,
Sep 12, 2013, 4:29:20 AM9/12/13
to Fumin Wang, Andrew Gerrand, Steven Blenkinsop, Rémy Oudompheng, GreatOdinsRaven, golang-nuts
On Thu, Sep 12, 2013 at 10:16 AM, Fumin Wang <awaw...@gmail.com> wrote:

I want to add one more comment:

I guess that in the early stage of Go design, 'new' was the only way
how to create a "dynamic" instance at run time. Later, syntactic sugar
was added to 'T{}' (ie. allowing &T{}) to ease cases where one needs
an address of a newly allocated composite literal as a shortcut to 'v
= new(T), *v = T{whatever}'.

Note that the above cannot be replaced by

var v0 = T{whatever}
v = &v0

As that is a static instance ('v0') only. For run time determined
number of instances, 'new', the underlying one in &T{}, is inevitable.

That's why I think that proposals to remove 'new' are not reasonable
as it is in a sense "primary", the syntactic sugar is built on top of
it (semantically) and the sugar should never IMO cause it's building
blocks to vanish.

-j

Fumin Wang

unread,
Sep 12, 2013, 9:41:03 AM9/12/13
to golan...@googlegroups.com, Fumin Wang, Andrew Gerrand, Steven Blenkinsop, Rémy Oudompheng, GreatOdinsRaven
Got it, I think this is one of the places where "new" is inevitable.
Although, I guess we can probably circumvent it by using slices of T, "new" may be read more natural in some cases. 
Thank you Jan for the insight.

Kevin Gillette

unread,
Sep 12, 2013, 1:50:23 PM9/12/13
to golan...@googlegroups.com, Fumin Wang, Andrew Gerrand, Steven Blenkinsop, Rémy Oudompheng, GreatOdinsRaven
On Thursday, September 12, 2013 2:29:20 AM UTC-6, Jan Mercl wrote:
Note that the above cannot be replaced by 
        var v0 = T{whatever} 
        v = &v0 
As that is a static instance ('v0') only. For run time determined 
number of instances, 'new', the underlying one in &T{}, is inevitable.
 
That's not strictly true. For one thing, variable number of instances can be handled rather simply as shown in: http://play.golang.org/p/o_dHf9Atgd. Functions as simple as the anonymous one in the playground snippet are recognized as inlineable by, at least, the tip compiler. Not only is there a better workaround in the typical case, as Fumin Wang mentioned, by piggybacking the allocation on slice allocation, but in many cases it's preferable to using new (such as with oft-touted "second order allocators"). new is only slightly more convenient than surrogate slice allocation for the runtime variable case and the escape-to-heap mechanism for static cases; since we can technically get away with not having new, I wouldn't call it inevitable.

Additionally, there has been much discussion concerning the idea of arbitrarily addressable expressions, through implicit use of an implicit intermediate var. The idea does not involve large hidden costs, but thus far has perhaps suffered from lack justifiable usefulness (since part of its role is already fulfilled by new). Getting rid of new, for Go2, would make addressable expressions much more appealing. Examples:

x := &int(3) // replaces `x := new(int); *x = 3`
x := &3      // illegal: constants are not addressable.
*&int(0) = 1 // compiler errors with "declared and not used", if not syntactically invalid

It would also have the benefit of making &T{} a non-special case.

Steven Blenkinsop

unread,
Sep 12, 2013, 4:32:59 PM9/12/13
to Andrew Gerrand, Rémy Oudompheng, GreatOdinsRaven, golang-nuts, Fumin Wang
On Thursday, September 12, 2013, Andrew Gerrand wrote:

It's not arbitrary. It's one of the fundamental distinctions between make and new. That's Rémy's point.

That's not true. Map, slice, and channel types have more in common with each other than with pointers. That's why they are all associated with the make function.
 
One can imagine designs of pointers which have to be implemented as a multi-word structure with a factory method in C, or which require a header to be allocated along with the data they point to which can't be represented in the language. You could also imagine a Go-like language where pointers and slices are unified, highlighting the similarities there. To me, this makes the distinctions you and Rémy are drawing seem rather more incidental (arbitrary was the wrong word) than fundamental. There are meaningful distinctions between make and new, but these arise from the designs of make and new themselves, which involve decisions which, while reasoned, are also subjective.

Rémy Oudompheng

unread,
Sep 12, 2013, 4:37:07 PM9/12/13
to Steven Blenkinsop, Andrew Gerrand, GreatOdinsRaven, golang-nuts, Fumin Wang
The whole point of Go is to forbid pointer arithmetic. Why would you
unify pointers and slices?

Rémy.


2013/9/12, Steven Blenkinsop <stev...@gmail.com>:

Steven Blenkinsop

unread,
Sep 12, 2013, 7:00:43 PM9/12/13
to Kevin Gillette, golan...@googlegroups.com, Fumin Wang, Andrew Gerrand, Rémy Oudompheng, GreatOdinsRaven
On Thursday, 12 September 2013, Rémy Oudompheng wrote:
The whole point of Go is to forbid pointer arithmetic. Why would you
unify pointers and slices?

Rémy.

I'm not saying you'd want to do it. Making it safe would mean having the overhead of passing around a slice every time you use a pointer. The point was more that the concepts are essentially compatible.

On Thursday, September 12, 2013, Kevin Gillette wrote:
 
Additionally, there has been much discussion concerning the idea of arbitrarily addressable expressions, through implicit use of an implicit intermediate var. The idea does not involve large hidden costs, but thus far has perhaps suffered from lack justifiable usefulness (since part of its role is already fulfilled by new). Getting rid of new, for Go2, would make addressable expressions much more appealing. Examples:

x := &int(3) // replaces `x := new(int); *x = 3`
x := &3      // illegal: constants are not addressable.
*&int(0) = 1 // compiler errors with "declared and not used", if not syntactically invalid

It would also have the benefit of making &T{} a non-special case.

This would have the unfortunate consequence of allowing people to accidentally write `T(x).F()` where they mean `(*T)(&x).F()`. I'm not sure how big of a deal this would be. I'd suggest pointer literals as an alternative (`(*T){5}`) instead, but this doesn't play well with type elision in slice and array literals (is `{x}` a pointer to a copy of `x`, or a pointer to a struct containing a copy of `x`?).

Kevin Gillette

unread,
Sep 12, 2013, 7:55:04 PM9/12/13
to golan...@googlegroups.com, Kevin Gillette, Fumin Wang, Andrew Gerrand, Rémy Oudompheng, GreatOdinsRaven
On Thursday, September 12, 2013 5:00:43 PM UTC-6, Steven Blenkinsop wrote:
This would have the unfortunate consequence of allowing people to accidentally write `T(x).F()` where they mean `(*T)(&x).F()`. I'm not sure how big of a deal this would be. I'd suggest pointer literals as an alternative (`(*T){5}`) instead, but this doesn't play well with type elision in slice and array literals (is `{x}` a pointer to a copy of `x`, or a pointer to a struct containing a copy of `x`?).

Generally speaking, in Go you can't do `(*T)(&x)` anyway, unless x is already a *T (e.g., unlike C, where you can get away with murder). That said, they can already accidentally write `T(x).F()` anyway. Furthermore, I can't see how addressable expressions would allow any of what you mentioned (beyond what is already allowed).

Having looked back at earlier threads (including the make vs new one), I'm rather dissatisfied with any suggestion that involves curly braces for any literal that isn't a composite literal, such as the oft-demonstrated `&int{5}`. Pointers, like ints, aren't composites at all -- they don't have multiple parts. If there were to be such a thing as a pointer literal, it should use parentheses rather than curly braces.

Steven Blenkinsop

unread,
Sep 12, 2013, 9:34:44 PM9/12/13
to Kevin Gillette, golan...@googlegroups.com, Fumin Wang, Andrew Gerrand, Rémy Oudompheng, GreatOdinsRaven
On Thursday, September 12, 2013, Kevin Gillette wrote:

Generally speaking, in Go you can't do `(*T)(&x)` anyway, unless x is already a *T (e.g., unlike C, where you can get away with murder). That said, they can already accidentally write `T(x).F()` anyway. Furthermore, I can't see how addressable expressions would allow any of what you mentioned (beyond what is already allowed).

It is actually quite common to convert between method sets on conversion compatible named types and pointers to them. You can only "accidentally" write `T(x).F()` now if `F` can be called on non-addressable values (i.e. has a `T` receiver), in which case it operates on a copy of `x` anyways, so there's no accident. You need to write `(*T)(&x)` in order to get `T`s pointer methods to operate on `x` in place. Making `T(x)` addressable would mean that `T(x).F()` would be valid, but that, if `F` is a pointer method, it would be operating on a copy of `x`, creating a source of potential errors.

Gustavo Niemeyer

unread,
Sep 12, 2013, 9:42:16 PM9/12/13
to Rémy Oudompheng, Steven Blenkinsop, Andrew Gerrand, GreatOdinsRaven, golang-nuts, Fumin Wang
On Thu, Sep 12, 2013 at 5:37 PM, Rémy Oudompheng
<remyoud...@gmail.com> wrote:
> The whole point of Go is to forbid pointer arithmetic.

Do we have a quotes page for Go yet? :-)

Steven Blenkinsop

unread,
Sep 12, 2013, 10:06:29 PM9/12/13
to Gustavo Niemeyer, Rémy Oudompheng, Andrew Gerrand, GreatOdinsRaven, golang-nuts, Fumin Wang
There's this, but as the banner says, it's no longer actively maintained. For reasons.

Dan Kortschak

unread,
Sep 12, 2013, 10:22:03 PM9/12/13
to Steven Blenkinsop, Gustavo Niemeyer, Rémy Oudompheng, Andrew Gerrand, GreatOdinsRaven, golang-nuts, Fumin Wang
On Thu, 2013-09-12 at 22:06 -0400, Steven Blenkinsop wrote:
> For reasons.
>
:(

Reply all
Reply to author
Forward
0 new messages