Another look at new(t) and &t{}

3,890 views
Skip to first unread message

chl

unread,
Aug 2, 2012, 5:35:09 AM8/2/12
to golan...@googlegroups.com
First, I need to say that this is not about new vs make, its something completely different.

Putting primitive types aside there are currently 3 ways for a person to creating a pointer to a struct type, t. Eg :

Example 1
a = new(t)

Example 2
a = &t{}

Example 3
a1 = t{}
a2 = &a1

My proposal here is to get rid of the first and second example above.

If I could drawn your attention to example 1. This is not a perfect way to create a pointer to a struct, because if your struct has any field which should be assign to values other than the default values you need to repeat yourself alot. Eg:

Let t be:
type t struct {
     a, b, c, d, e int
}

you'll need to do this:
v := new(t)
v.a = 1
v.b = 2
v.c = 3
v.d = 4
v.e = 5

Notice that we had to repeat the variable name every single time, and that is in my view fairly repetitive. The normal way of creating a struct is less repetitive and is therefore, in my view, better.

If I may now draw your attention to example 2. &t{} is an exceptional case, meaning that normally it should not work, but we've written the compiler in such a way that it works. There are really no arguments for allowing this exception other than, the fact it brings with it a little convenience. The cost of  this construct though, is that, it makes it harder for a reader of the code to distinguish between a pointer to a t and a value t. Eg:

Let t be define as previously defined.
s1 := &t{1, 2, 3, 4, 5}
s2 := t{1, 2, 3, 4, 5}

This is easy to distinguish here because there is no code surrounding it, but if you imagine that this is one line in a page of code that is 1000's lines long, it becomes hard to determine if a variable is a pointer or an actual value. This problem is further exaggerated, if t is replaced with a longer name or when values for fields and field names are longer, as this drawn the readers attention away from the leading ampersand even more.


The result of this proposal is that:
1) all values and pointers will be created in the same way, reducing the cognitive load on the reader.
2) removing the new() keyword, makes the language simpler.
3) prevent people from using pointers unecessarily.
4) draw attention to parts of the code that actually use pointers, to simplify debugging.

Therefore, this 2 constructs (example 1, and example 2) should be removed. This however is not without its downsides.

The result of this proposal will make it harder to create a pointer to a type t, because now if you want to create a pointer to a type t you'll have to do this:

s := t{1, 2, 3, 4, 5}
s1 := &s

 This is inherently less efficient because you'll need to allocate space in memory, to store s and another to store a pointer to s. 

 The other downside to this is that existing code would need to be changed. So I don't see this change happening in Go1. But probably in Go2.

However, if you would look at this situation from a slightly different perspective, the cons to the proposal are actually pros in the majority of situations.

First, the odd construct, although slightly more verbose, drawn the readers attention to it, making it easier for the reader to determine when a variable is a pointer. 

Second, it discourages people from using pointers when not needed. Using less pointers makes it easier to reason about code, and result in less bugs. Case in point:

Let num(*int), be a function that adds 1 to the incoming value.
n := 1
num(&n)
fmt.Println(n)

because pointers are harder to create, people would use & more often when invoking a function that requires pointer. This allows the reader to determine, if the function changes the value without looking at the prototype of the function.

When pointers are truly needed (linked list, trees) , people could create their own functions that create pointers. Eg:

func NewInt(n int) *int {
return &n
}

This kinds of functions are trivial to write, but are more powerful than a new() function that covers all cases. In the example above, NewInt() can create a *int, with any value, this is better than the the builtin new() function, if you are going to assign the value the pointer is pointing to to something other than the default value. It should be noted that,  there are no performance penalty to this because small functions are inlined during compilation.



To summarize i am proposing 2 things:
1) Removal of the new() builtin function
2) Removal of the &t{} construct.

Jan Mercl

unread,
Aug 2, 2012, 5:55:03 AM8/2/12
to chl, golan...@googlegroups.com
On Thu, Aug 2, 2012 at 11:35 AM, chl <chiah...@gmail.com> wrote:
To summarize i am proposing 2 things:
1) Removal of the new() builtin function

Can't imagine that for every built-in type and every non built-in type whose underlying type is a built-in type, one would now have to write a concrete-type taking function replacing the single predefined new().
 
2) Removal of the &t{} construct.

It makes IMO no trouble at all and it's part of a common Go idiom. Just look how often it is used in the standard library (and in any good written Go code). It's an essential feature of the language. Can't imagine this to happen either.

To summarize: Please no.

-j

Zippoxer

unread,
Aug 2, 2012, 6:34:23 AM8/2/12
to golan...@googlegroups.com
I agree with the first proposal.

I haven't made a decision about the second proposal, but I'd like to point out that preventing &T won't be weird since &1 and &"a" are already not possible (for example). I cannot see how important are the pros of doing so, since I have never made a pointer just because it was one keystroke away, and this is not obvious to me that there are people who do so, so I'm waiting to hear others.

I think the problems you are trying to solve are solved by this proposal, but they're just not real problems. When I say real problem I mean difficult problem or frequent problem. I never had to figure out whether a variable is a pointer or not. That's a lie, I most probably did but it didn't bother me enough to remember that. The standard library has many individual files that are over 1000 lines long, so the Go team must know whether "pointer or not?" is a real problem.

roger peppe

unread,
Aug 2, 2012, 6:44:05 AM8/2/12
to chl, golan...@googlegroups.com
this is a silly idea. it's easy to see the "&".

also, it would break almost all Go code. here's a count
of just the places where a pointer to a struct
is created and immediately returned:

% find $GOROOT -name '*.go' | xargs egrep 'return &[a-zA-Z]+{' | wc
361 1926 25785
%

Jan Mercl

unread,
Aug 2, 2012, 6:47:57 AM8/2/12
to Zippoxer, golan...@googlegroups.com
On Thu, Aug 2, 2012 at 12:34 PM, Zippoxer <zipp...@gmail.com> wrote:
I agree with the first proposal.

It's used a lot in the standard library: 

(12:40) jnml@celsius:~/go/src/pkg$ hg id
5e806355a9e1 (release-branch.go1) go1.0.2/release
(12:40) jnml@celsius:~/go/src/pkg$ grep -nr --include \*.go new\(.*\) * | wc -l
912
(12:40) jnml@celsius:~/go/src/pkg$


I haven't made a decision about the second proposal, but I'd like to point out that preventing &T won't be weird since &1 and &"a" are already not possible (for example). I cannot see how important are the pros of doing so, since I have never made a pointer just because it was one keystroke away, and this is not obvious to me that there are people who do so, so I'm waiting to hear others.

It's used a lot in the standard library:

(12:45) jnml@celsius:~/go/src/pkg$ egrep -nr --include \*.go '&[a-zA-Z_]+{.*}' * | wc -l
989
(12:46) jnml@celsius:~/go/src/pkg$ 

-j

chl

unread,
Aug 2, 2012, 6:53:16 AM8/2/12
to golan...@googlegroups.com, chl
That i agree is true, but that is actually a non-issue, because it is go-fixable.

Aram Hăvărneanu

unread,
Aug 2, 2012, 6:53:14 AM8/2/12
to roger peppe, chl, golan...@googlegroups.com
> here's a count
> of just the places where a pointer to a struct
> is created and immediately returned:
>
> % find $GOROOT -name '*.go' | xargs egrep 'return &[a-zA-Z]+{' | wc
> 361 1926 25785
> %

and here is a count where a pointer to an initialized struct is
created and we only care about the pointer:

white:aram$ g '&[a-zA-Z].*{[^}]' `lsr ~/go/src/{cmd,pkg}` | wc -l
1566

This idiom is extremely common, useful, and nice.

--
Aram Hăvărneanu

Aram Hăvărneanu

unread,
Aug 2, 2012, 6:57:52 AM8/2/12
to roger peppe, chl, golan...@googlegroups.com
> and here is a count where a pointer to an initialized struct is
> created and we only care about the pointer:
>
> white:aram$ g '&[a-zA-Z].*{[^}]' `lsr ~/go/src/{cmd,pkg}` | wc -l
> 1566

Stupid typo, it's only 1206:

white:aram$ g '&[a-zA-Z]+{[^}]' `lsr ~/go/src/{cmd,pkg}` | wc -l
1206

> That i agree is true, but that is actually a non-issue, because it is
> go-fixable.

It is not go fixable. It's very common to create an initialize a deep
data structure that embeds many structs using this idiom. Your
proposal would make this impossible, you'd have to initialize it at
runtime. This is not Pascal.

The lack of the &lit idiom yields uglier code.

--
Aram Hăvărneanu

chris dollin

unread,
Aug 2, 2012, 7:03:10 AM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 10:35, chl <chiah...@gmail.com> wrote:

Generally: I see no reason to introduce these gratuitous incompatabilities
into an eventual Go 2 -- I say "gratuitous" becase I don't thing there's
any real benefit in your proposal.

> If I may now draw your attention to example 2. &t{} is an exceptional case,
> meaning that normally it should not work, but we've written the compiler in
> such a way that it works.

The compiler is only doing what the spec says. It's not some kind
of accident.

> There are really no arguments for allowing this
> exception other than, the fact it brings with it a little convenience.

It's not "little"; it's *very* convenient to not have to introduce a
new variable
just so that we can use its address.

> The
> cost of this construct though, is that, it makes it harder for a reader of
> the code to distinguish between a pointer to a t and a value t. Eg:
>
> Let t be define as previously defined.
> s1 := &t{1, 2, 3, 4, 5}
> s2 := t{1, 2, 3, 4, 5}
>
> This is easy to distinguish here because there is no code surrounding it,
> but if you imagine that this is one line in a page of code that is 1000's
> lines long, it becomes hard to determine if a variable is a pointer or an
> actual value.

I simply don't think that this is a problem Go programmers will
have.

> s := t{1, 2, 3, 4, 5}
> s1 := &s
>
> This is inherently less efficient because you'll need to allocate space in
> memory, to store s and another to store a pointer to s.

That depends on compiler smarts; it would be easy to arrange that
this generated the same code as the present `s1 := &t{1,2,3,4,5}`,
if s was not otherwise used.

> First, the odd construct, although slightly more verbose, drawn the readers
> attention to it, making it easier for the reader to determine when a
> variable is a pointer.

The & is there right at the front. I don't see who pointerness could be
more obviously indicated.

> Second, it discourages people from using pointers when not needed.

And from using them when they /are/ needed, leading to more bugs.

Pickyness in the syntax is no substitute for knowing how to use the
language.

> This allows the reader to
> determine, if the function changes the value without looking at the
> prototype of the function.

You'd want to lok at the documentation for a function you're using,
at which point you see the prototype anyway.

Chris

--
Chris "allusive" Dollin

Zippoxer

unread,
Aug 2, 2012, 7:04:52 AM8/2/12
to golan...@googlegroups.com, Zippoxer
I agree with the first proposal if it will be applied in Go 2 and gofix (or any other tool) will replace new(T) with &T{}.

I don't agree with the second proposal since I just realized it's pain but no gain.

chris dollin

unread,
Aug 2, 2012, 7:09:45 AM8/2/12
to Zippoxer, golan...@googlegroups.com
On 2 August 2012 12:04, Zippoxer <zipp...@gmail.com> wrote:
> I agree with the first proposal if it will be applied in Go 2 and gofix (or
> any other tool) will replace new(T) with &T{}.

Bad idea, or at least incomplete: new(int).

Jan Mercl

unread,
Aug 2, 2012, 7:09:53 AM8/2/12
to Zippoxer, golan...@googlegroups.com
On Thu, Aug 2, 2012 at 1:04 PM, Zippoxer <zipp...@gmail.com> wrote:
I agree with the first proposal if it will be applied in Go 2 and gofix (or any other tool) will replace new(T) with &T{}.

And 'new(int)'  -> '&int{}' ???

-j

Zippoxer

unread,
Aug 2, 2012, 7:14:20 AM8/2/12
to golan...@googlegroups.com, Zippoxer, ehog....@googlemail.com
Hey, gofix is not stupid.

chl

unread,
Aug 2, 2012, 7:28:29 AM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
As pointed out by aram, the &t{} idiom is used commonly in deep nested structures. But isn't that fairly inefficient. That is you need to allocated space for a variable, than a pointer to variable, so every field, thats nested has a 4byte overhead. Isn't that unnecessary use of pointers?


The compiler is only doing what the spec says. It's not some kind
of accident.

I am not saying that its an accident, I know it was written that way as i've stated. Its just that its exception to the general rule, which makes it inconsistent with the rest of the language.

It's not "little"; it's *very* convenient to not have to introduce a
new variable
just so that we can use its address.

If i am not wrong, that is actually what the &t{} idiom is currently doing. I'm just making it explicit. If you really don't want to use a new variable you can capture that behavior in a function.
 
And i'm not saying i don't like pointers. I use them alot in c and c++. The problems is that it becomes a problem when we try to program concurrently, that is if they are used carelessly.

Though I must say that its not true that you have to create a function to create a pointer for every in your program, because you don't really need to create pointers that often.

chris dollin

unread,
Aug 2, 2012, 7:43:15 AM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 12:28, chl <chiah...@gmail.com> wrote:

> As pointed out by aram, the &t{} idiom is used commonly in deep nested
> structures. But isn't that fairly inefficient.

No.

> That is you need to allocated
> space for a variable, than a pointer to variable, so every field, thats
> nested has a 4byte overhead. Isn't that unnecessary use of pointers?

If you need the pointers for the fields of the struct, you need the
pointers. If you don't, you're not using them. I don't see an inefficiency
in the use of the &T{} construct.

> I am not saying that its an accident, I know it was written that way as i've
> stated. Its just that its exception to the general rule, which makes it
> inconsistent with the rest of the language.

It's not inconsistent, it just not uniform, and that nonuniformity is in
service to more straightforward code. If that's the price, I'll have two.

> If i am not wrong, that is actually what the &t{} idiom is currently doing.
> I'm just making it explicit.

Yes. That's what we don't want or need. The & is enough explicitness
for the job.

> If you really don't want to use a new variable
> you can capture that behavior in a function.

What, not only do I have to have an explicit variable, I have to
introduce a trivial function -- just to disallow &T{}?

That doesn't seem like a good idea to me. (There are /good/ reasons
for introducing constructor functions, but not liking the &T{} syntax
doesn't seem to me to be one of them.

> And i'm not saying i don't like pointers. I use them alot in c and c++. The
> problems is that it becomes a problem when we try to program concurrently,
> that is if they are used carelessly.

Then we should not use pointers (and concurrency) carelessly.

> Though I must say that its not true that you have to create a function to
> create a pointer for every in your program, because you don't really need to
> create pointers that often.

If we're not creating pointers, then the whole proposal is irrelevant. It's
only pointerful structures that it applies to.

There's a place for a discussion about when, and when not, to be using
pointers, but the opening post is exactly about which ways to create
pointer values.

Jan Mercl

unread,
Aug 2, 2012, 7:43:31 AM8/2/12
to chl, golan...@googlegroups.com, ehog....@googlemail.com
On Thu, Aug 2, 2012 at 1:28 PM, chl <chiah...@gmail.com> wrote:
As pointed out by aram, the &t{} idiom is used commonly in deep nested structures. But isn't that fairly inefficient. That is you need to allocated space for a variable, than a pointer to variable, so every field, thats nested has a 4byte overhead. Isn't that unnecessary use of pointers?

Not where appropriate. Sometimes it's by design and the better of the possible ones. Linked list anyone?
 
The compiler is only doing what the spec says. It's not some kind
of accident.

I am not saying that its an accident, I know it was written that way as i've stated. Its just that its exception to the general rule, which makes it inconsistent with the rest of the language.

I can easily find the rule about taking the address of a composite literal in the specs. I fail to find the "general rule" in the same  ;-)
 

It's not "little"; it's *very* convenient to not have to introduce a
new variable
just so that we can use its address.

If i am not wrong, that is actually what the &t{} idiom is currently doing. I'm just making it explicit. If you really don't want to use a new variable you can capture that behavior in a function.

&T{} is IMO as explicit as explicitness can get.
 
 
And i'm not saying i don't like pointers. I use them alot in c and c++. The problems is that it becomes a problem when we try to program concurrently, that is if they are used carelessly.

No difference compared to careless updating of e.g. an int (or any non pointer) typed variable.
 

Though I must say that its not true that you have to create a function to create a pointer for every in your program, because you don't really need to create pointers that often.

Little sense this claim makes. The code for helloworld.go doesn't need pointers at all. Some apps/packages use pointers heavily and for perfectly good reasons. Some others even cannot be coded w/o pointers in a sane way. There's nothing generic about this and it can hardly be used as an argument for anything.

-j

chl

unread,
Aug 2, 2012, 7:51:38 AM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com

Little sense this claim makes. The code for helloworld.go doesn't need pointers at all. Some apps/packages use pointers heavily and for perfectly good reasons. Some others even cannot be coded w/o pointers in a sane way. There's nothing generic about this and it can hardly be used as an argument for anything.

-j

Its kinda funny, but you are actually contradicting yourself, because your the one that said that we would need to write a function for every type. :p

On Thursday, August 2, 2012 5:55:03 PM UTC+8, Jan Mercl wrote:
On Thu, Aug 2, 2012 at 11:35 AM, chl <chiah...@gmail.com> wrote:
To summarize i am proposing 2 things:
1) Removal of the new() builtin function

Jan Mercl

unread,
Aug 2, 2012, 7:58:39 AM8/2/12
to chl, golan...@googlegroups.com, ehog....@googlemail.com
On Thu, Aug 2, 2012 at 1:51 PM, chl <chiah...@gmail.com> wrote:

Little sense this claim makes. The code for helloworld.go doesn't need pointers at all. Some apps/packages use pointers heavily and for perfectly good reasons. Some others even cannot be coded w/o pointers in a sane way. There's nothing generic about this and it can hardly be used as an argument for anything.

-j

Its kinda funny, but you are actually contradicting yourself, because your the one that said that we would need to write a function for every type. :p

Can't see where. Never mind, maybe there's something "lost in translation".

-j

chl

unread,
Aug 2, 2012, 10:07:44 AM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
If you need the pointers for the fields of the struct, you need the
pointers. If you don't, you're not using them. I don't see an inefficiency
in the use of the &T{} construct.

Well again you've misunderstood what I meant. I', not saying that pointers are inefficient, but in quite a lot of code, i've seen people seem use pointers even if they don't need to just because they can. Well than again, i see your point that the &T{} construct is not the problem here.

It's not inconsistent, it just not uniform, and that nonuniformity is in
service to more straightforward code. If that's the price, I'll have two.

if its not? I mean straightforward code is subjective. Something that seems straightforward to one person may not be seem straightforward to another.

Yes. That's what we don't want or need. The & is enough explicitness
for the job.
 
What, not only do I have to have an explicit variable, I have to
introduce a trivial function -- just to disallow &T{}?

Its not just to replace &T{}. Writing a trivial function removes the need for both &T{} and new(). And it also allows you to produce pointers for both primitive types, alias of primitive types and struct types in the same way. Meaning that you don't need choose the way in which you create a pointer. 
 

That doesn't seem like a good idea to me. (There are /good/ reasons
for introducing constructor functions, but not liking the &T{} syntax
doesn't seem to me to be one of them.

Once again its not about not liking the &T{} syntax. Its about uniformity. If we could produce a primitive type with the same syntax i'll be all for it.


If we're not creating pointers, then the whole proposal is irrelevant. It's
only pointerful structures that it applies to.

There's a place for a discussion about when, and when not, to be using
pointers, but the opening post is exactly about which ways to create
pointer values.

Chris

--
Chris "allusive" Dollin

Unless i don't know what i am typing myself, the opening post was about how not to create pointers. And if we are not creating pointer wouldn't it make sense that we don't have 3 different ways of creating the same pointer for the same type. 

yy

unread,
Aug 2, 2012, 10:22:55 AM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 16:07, chl <chiah...@gmail.com> wrote:
> in quite a lot of code, i've seen people seem use
> pointers even if they don't need to just because they can.

I have not noticed this. Would you mind giving some examples? What I
have sporadically observed is some confusion in users who come from
dynamic languages and have not had any exposure to pointers before,
but I don't think that this problem is caused by Go syntax.


--
- yiyus || JGL .

chl

unread,
Aug 2, 2012, 10:55:18 AM8/2/12
to golan...@googlegroups.com, chl
The last one i've seen is something like this:
doc := &Document{
"Test page",
Content{
&Paragraph{
Content{
&LeafElement{"This"},
&Italic{Content{&LeafElement{"is"}}},
&Bold{Content{&LeafElement{"a"},
&Italic{Content{&LeafElement{"test."}}}}},
},
},
},
}


This is not my code btw. And what you've said is quite true, so i may be wrong there. Though i don't think that Go's syntax is wrong, I just think that maybe we could make it more consistent so that you don't have to know that & does not work on primitive types and new() does not work that well with structs. And at same time removing a keyword ("new()") that isn't used quite often used.

Its definitely harder to use a single method to create pointers, but that consistency will help reduce the time needed for people to learn language, as well as make the language, and compiler. It also make code written by other people more straightforward, as you don't need to see, people creating pointers in 3 different styles. You can just recognize patterns in code instead.

Aram Hăvărneanu

unread,
Aug 2, 2012, 10:57:21 AM8/2/12
to chl, golan...@googlegroups.com, ehog....@googlemail.com
> Writing a trivial function removes the need for both &T{} and new().

Please write the trivial function that helps initializing this:

type Object struct {
Kind string
Name string
Data interface{}
Type string
Children []*Object
}

var Ast = &Object{
Kind: "package",
Name: "main",
Children: []*Object{
{
Kind: "vardecl",
Name: "foo",
Data: 42,
Type: "int",
},
{
Kind: "funcdecl",
Name: "bar",
Data: &Object{

Kind: "blockstmt",
Data: "{}",
},
},
{
Kind: "typedecl",
Name: "baz",
Data: &Object{
Kind: "structlit",
Children: []*Object{
{
Kind: "field",
Name: "one",
Type: "string",
},
{
Kind: "field",
Name: "two",
Type: "int",
},
},
},
},
},
}

Not only that it would be harder to read and the functions would be
far from trivial, but you'd lose the very nice property of static
(compile time) initialization.

--
Aram Hăvărneanu

Ian Lance Taylor

unread,
Aug 2, 2012, 11:02:20 AM8/2/12
to chl, golan...@googlegroups.com
On Thu, Aug 2, 2012 at 7:55 AM, chl <chiah...@gmail.com> wrote:
>
> This is not my code btw. And what you've said is quite true, so i may be
> wrong there. Though i don't think that Go's syntax is wrong, I just think
> that maybe we could make it more consistent so that you don't have to know
> that & does not work on primitive types and new() does not work that well
> with structs. And at same time removing a keyword ("new()") that isn't used
> quite often used.

Just a side comment: note that "new" is not a keyword. It's a
predeclared function. It's entirely valid to write code like

func f() int {
new := 1
return new + 2
}

or

func new() *T {
return &T{}
}

That is, "new" only takes on its special meaning if you don't declare
it yourself.

Ian

chris dollin

unread,
Aug 2, 2012, 11:10:32 AM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 15:07, chl <chiah...@gmail.com> wrote:

>> It's not inconsistent, it just not uniform, and that nonuniformity is in
>> service to more straightforward code. If that's the price, I'll have two.
>
> if its not? I mean straightforward code is subjective. Something that seems
> straightforward to one person may not be seem straightforward to another.

Requiring that the construct

return me a pointer to a new piece of store initialised this way

be written as

make me a variable v initialised this way
return me a pointer to v

seems pretty transparently less straightforward. v is just a
placeholder variable with no other use, clutter to trip over.

>> What, not only do I have to have an explicit variable, I have to
>> introduce a trivial function -- just to disallow &T{}?
>
> Its not just to replace &T{}. Writing a trivial function removes the need
> for both &T{} and new().

And makes the code clunkier -- more verbose, with more to keep
track of. If every trivial pointer-to-struct construction "needs" a new
function, the fact that these is a function no longer tells us anything.
The current system allows us to recognise trivial constructions as
trivial, trivially.

> And it also allows you to produce pointers for both
> primitive types, alias of primitive types and struct types in the same way.
> Meaning that you don't need choose the way in which you create a pointer.

Well, I don't need to choose the way I create a pointer [to a new
struct] -- I use &T{}. I've never needed the semantics that new gives
me, and I wouldn't consider using `v := T{...}; pv := &v` [or `return &v`]
because, well, why would I want to?

> Once again its not about not liking the &T{} syntax. Its about uniformity.

Uniformity is not automatically a virtue. Uniformity would dispense with
:=. Uniformity would require that pointer-receiver methods when applied
to a variable v would require writing (&v).method. Uniformity would have
all our integer literals written in octal (or hex, or decimal). Uniformity would
have us type each element of an argument list separately. Uniformity would
require that all mathematical expressions took exactly two simple
operands.

It's a tradeoff. It's tradeoffs all the way down.

>> There's a place for a discussion about when, and when not, to be using
>> pointers, but the opening post is exactly about which ways to create
>> pointer values.

> Unless i don't know what i am typing myself, the opening post was about how
> not to create pointers.

]] Putting primitive types aside there are currently 3 ways for a person to
]] creating a pointer to a struct type, t. Eg :

It's about ways for programs in Go to create pointer values. You presented
three of the existing ones and said we should throw two of them away.

chris dollin

unread,
Aug 2, 2012, 11:15:55 AM8/2/12
to Aram Hăvărneanu, chl, golan...@googlegroups.com
That looks like run-time initialisation to me.

http://golang.org/ref/spec#Program_initialization_and_execution

Chris

(Its convenience and clarity I do not deny.)

--
Chris "allusive" Dollin

chl

unread,
Aug 2, 2012, 11:36:23 AM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
you mean something like this:

func newObject(kind, name, t string, data interface{}, child []*Object) *Object {
v := Object{
Kind: kind,
Name: name,
Type, t,
Data:     data,
Childrem: child,
}
return &v
}

var Ast = newObject("package", "main", "", nil, []*Object{
newObject("vardecl", "foo", "int", 42, nil),
newObject("funcdecl", "bar", "", newObject("blockstmt", "", "", "{}", nil), nil),
newObject("typedcl", "baz", "",
newObject("structlit", "", "", nil, []*Object{
newObject("field", "one", "string", nil, nil),
newObject("field", "two", "int", nil, nil),
}),
nil),
})

The basic function is still trivial though. Plus i don't understand why you need that many pointers.

chl

unread,
Aug 2, 2012, 11:37:45 AM8/2/12
to golan...@googlegroups.com, chl
Sorry that's a slip on my part.

chl

unread,
Aug 2, 2012, 11:49:11 AM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
Requiring that the construct

    return me a pointer to a new piece of store initialised this way

be written as

    make me a variable v initialised this way
    return me a pointer to v

seems pretty transparently less straightforward. v is just a
placeholder variable with no other use, clutter to trip over.

Use a function ?

And makes the code clunkier -- more verbose, with more to keep
track of. If every trivial pointer-to-struct construction "needs" a new
function, the fact that these is a function no longer tells us anything.
The current system allows us to recognise trivial constructions as
trivial, trivially.

In some ways, we already have alot of new functions (ie type constructors(at least thats what i call them)). You dont' need a constructor unless you are explicitly initializing a pointer as a variable. You can very trivially pass a pointer to a function using & in front of every variable name, without relying on another function.

I think you're grossly overestimating the number of times you'll have to write an additional function. Because in most cases you do not need to.

Well, I don't need to choose the way I create a pointer [to a new
struct] -- I use &T{}. I've never needed the semantics that new gives
me, and I wouldn't consider using `v := T{...}; pv := &v` [or `return &v`]
because, well, why would I want to?

Well that's true for you. But i also know people that swears by the new() function. They would probably have a different opinion.


Uniformity is not automatically a virtue. Uniformity would dispense with
:=. Uniformity would require that pointer-receiver methods when applied
to a variable v would require writing (&v).method. Uniformity would have
all our integer literals written in octal (or hex, or decimal). Uniformity would
have us type each element of an argument list separately. Uniformity would
require that all mathematical expressions took exactly two simple
operands.

It's a tradeoff. It's tradeoffs all the way down. 

Not always true, but it often is. 

Though if you ask me i wouldn't get rid of :=, i like it, alot. Getting rid of var i int, would be much better, (just joking).

It's about ways for programs in Go to create pointer values. You presented
three of the existing ones and said we should throw two of them away.

And why again do we need 3 ways of doing the same thing? 

Michael Jones

unread,
Aug 2, 2012, 11:49:38 AM8/2/12
to chl, golan...@googlegroups.com
I have data structures with ~1,00,000 pointers that are essential to
my algorithms, so the "minimize pointers" argument is lost to me.
Thankfully, I don't allocate them as compile-time values. ;-)

--
Michael T. Jones | Chief Technology Advocate | m...@google.com | +1
650-335-5765

roger peppe

unread,
Aug 2, 2012, 12:02:42 PM8/2/12
to chl, golan...@googlegroups.com, ehog....@googlemail.com
On 2 August 2012 16:36, chl <chiah...@gmail.com> wrote:
> you mean something like this:
>
> func newObject(kind, name, t string, data interface{}, child []*Object)
> *Object {
> v := Object{
> Kind: kind,
> Name: name,
> Type, t,
> Data: data,
> Childrem: child,
> }
> return &v
> }
>
> var Ast = newObject("package", "main", "", nil, []*Object{
> newObject("vardecl", "foo", "int", 42, nil),
> newObject("funcdecl", "bar", "", newObject("blockstmt", "", "", "{}", nil),
> nil),
> newObject("typedcl", "baz", "",
> newObject("structlit", "", "", nil, []*Object{
> newObject("field", "one", "string", nil, nil),
> newObject("field", "two", "int", nil, nil),
> }),
> nil),
> })

that loses the nice property of the original that it's
easy to see which value corresponds to which field of the struct.

in my experience it's also common to initialise
some fields only, leaving others zero, where using
a function requires all fields all the time.

i can't really believe we're actually having this discussion.

chris dollin

unread,
Aug 2, 2012, 12:38:53 PM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 16:49, chl <chiah...@gmail.com> wrote:
>> Requiring that the construct
>>
>> return me a pointer to a new piece of store initialised this way
>>
>> be written as
>>
>> make me a variable v initialised this way
>> return me a pointer to v
>>
>> seems pretty transparently less straightforward. v is just a
>> placeholder variable with no other use, clutter to trip over.
>
> Use a function ?

(a) You have the same problem inside the function.

(b) And now you've made the code less obvious; a function might do
anything, but a &T{} does one known thing.

(c) Calls to the function must give all the arguments. A &T{K: V,...}
construct need only give the non-zero arguments and comes with
built-in clues as to the meanings of the values.

>> And makes the code clunkier -- more verbose, with more to keep
>> track of. If every trivial pointer-to-struct construction "needs" a new
>> function, the fact that these is a function no longer tells us anything.
>> The current system allows us to recognise trivial constructions as
>> trivial, trivially.
>
> In some ways, we already have alot of new functions (ie type constructors(at
> least thats what i call them)).

Constructor functiions are not limited to constructing pointer values.

> You dont' need a constructor unless you are
> explicitly initializing a pointer as a variable.

You don't /need/ the constructor at all; you can declare a struct
variable and take its address just fine. It's just a way to hide no
being allowed to use the &T{} syntax.

> You can very trivially pass
> a pointer to a function using & in front of every variable name, without
> relying on another function.

Well, yes, but ... what?

> I think you're grossly overestimating the number of times you'll have to
> write an additional function. Because in most cases you do not need to.

You don't /need/ too at all. It just makes the code easier to read.

> Well that's true for you. But i also know people that swears by the new()
> function. They would probably have a different opinion.

They, too, have already made their choice (a choice the original
proposal denies them). [I'm surpised that there would be much use
for new(T) at all, except perhaps for T=a primitive type.]

>> It's about ways for programs in Go to create pointer values. You presented
>> three of the existing ones and said we should throw two of them away.
>
> And why again do we need 3 ways of doing the same thing?

We have three ways each doing a different thing.

new(T) gives you a pointer to a zero-valued T. You can tell
that the zero-ness is deliberate.

&T{} gives you a pointer to an (possibly, partially) initialised
(non-primitive) T.

&t gives you a pointer to an already-existing addressable thing t.

chris dollin

unread,
Aug 2, 2012, 12:41:16 PM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 16:36, chl <chiah...@gmail.com> wrote:

> The basic function is still trivial though. Plus i don't understand why you
> need that many pointers.

Presumably the store-sharing is important in his problem.

chl

unread,
Aug 2, 2012, 12:53:50 PM8/2/12
to golan...@googlegroups.com, chl
Haha, that sure is alot of pointers to deal with.

chl

unread,
Aug 2, 2012, 12:57:08 PM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
Thats just an example. You can do exactly what you want if you change the constructor function to this:

func newObject(o Object) *Object {
return &o
}

This way you can do this:

a := newObject(Object {
               Kind: "Hi",
               Type: "int",
               })


And if we do get generics we could do something like this:

func ptr(t Type) *Type {
      return &t

chris dollin

unread,
Aug 2, 2012, 1:02:07 PM8/2/12
to chl, golan...@googlegroups.com
On 2 August 2012 17:57, chl <chiah...@gmail.com> wrote:
> Thats just an example. You can do exactly what you want if you change the
> constructor function to this:
>
> func newObject(o Object) *Object {
> return &o
> }
>
> This way you can do this:
>
> a := newObject(Object {
> Kind: "Hi",
> Type: "int",
> })

This is no improvement over

a := &Object{Kind: "Hi", Type: "int"}

or even

a := &Object{
Kind: "Hi",
Type: "int",
}

Zippoxer

unread,
Aug 2, 2012, 1:15:38 PM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
If we get generics, it would be forbidden to use ptr(t Type) according to your claiming. If s1 := &t{1, 2, 3, 4, 5} is not readable, how is  s1 := ptr(t{1, 2, 3, 4, 5} ) more readable?

kortschak

unread,
Aug 2, 2012, 8:20:31 PM8/2/12
to golan...@googlegroups.com
Please no. The use of ampersand gives clear visual cues and requires fewer keystrokes.
Message has been deleted

chl

unread,
Aug 2, 2012, 9:14:33 PM8/2/12
to golan...@googlegroups.com, chl, ehog....@googlemail.com
To a certain extent that is true. However, if you use an ide you can just look for the phrase "ptr" and you'll be able to find every single pointer. That however is impossible currently because you would miss the pointer for primitive types.

chl

unread,
Aug 2, 2012, 9:18:44 PM8/2/12
to golan...@googlegroups.com
Well i guess, i will stop the discussions here cause no one is interested. I apologize if I offended any persons feelings. And thank everyone for your time.

On Thursday, August 2, 2012 5:35:09 PM UTC+8, chl wrote:
First, I need to say that this is not about new vs make, its something completely different.

Putting primitive types aside there are currently 3 ways for a person to creating a pointer to a struct type, t. Eg :

Example 1
a = new(t)

Example 2
a = &t{}

Example 3
a1 = t{}
a2 = &a1

My proposal here is to get rid of the first and second example above.

If I could drawn your attention to example 1. This is not a perfect way to create a pointer to a struct, because if your struct has any field which should be assign to values other than the default values you need to repeat yourself alot. Eg:

Let t be:
type t struct {
     a, b, c, d, e int
}

you'll need to do this:
v := new(t)
v.a = 1
v.b = 2
v.c = 3
v.d = 4
v.e = 5

Notice that we had to repeat the variable name every single time, and that is in my view fairly repetitive. The normal way of creating a struct is less repetitive and is therefore, in my view, better.

If I may now draw your attention to example 2. &t{} is an exceptional case, meaning that normally it should not work, but we've written the compiler in such a way that it works. There are really no arguments for allowing this exception other than, the fact it brings with it a little convenience. The cost of  this construct though, is that, it makes it harder for a reader of the code to distinguish between a pointer to a t and a value t. Eg:

Let t be define as previously defined.
s1 := &t{1, 2, 3, 4, 5}
s2 := t{1, 2, 3, 4, 5}

This is easy to distinguish here because there is no code surrounding it, but if you imagine that this is one line in a page of code that is 1000's lines long, it becomes hard to determine if a variable is a pointer or an actual value. This problem is further exaggerated, if t is replaced with a longer name or when values for fields and field names are longer, as this drawn the readers attention away from the leading ampersand even more.


The result of this proposal is that:
1) all values and pointers will be created in the same way, reducing the cognitive load on the reader.
2) removing the new() keyword, makes the language simpler.
3) prevent people from using pointers unecessarily.
4) draw attention to parts of the code that actually use pointers, to simplify debugging.

Therefore, this 2 constructs (example 1, and example 2) should be removed. This however is not without its downsides.

The result of this proposal will make it harder to create a pointer to a type t, because now if you want to create a pointer to a type t you'll have to do this:

s := t{1, 2, 3, 4, 5}
s1 := &s

 This is inherently less efficient because you'll need to allocate space in memory, to store s and another to store a pointer to s. 

 The other downside to this is that existing code would need to be changed. So I don't see this change happening in Go1. But probably in Go2.

However, if you would look at this situation from a slightly different perspective, the cons to the proposal are actually pros in the majority of situations.

First, the odd construct, although slightly more verbose, drawn the readers attention to it, making it easier for the reader to determine when a variable is a pointer. 

Second, it discourages people from using pointers when not needed. Using less pointers makes it easier to reason about code, and result in less bugs. Case in point:

Let num(*int), be a function that adds 1 to the incoming value.
n := 1
num(&n)
fmt.Println(n)

because pointers are harder to create, people would use & more often when invoking a function that requires pointer. This allows the reader to determine, if the function changes the value without looking at the prototype of the function.

When pointers are truly needed (linked list, trees) , people could create their own functions that create pointers. Eg:

func NewInt(n int) *int {
return &n
}

This kinds of functions are trivial to write, but are more powerful than a new() function that covers all cases. In the example above, NewInt() can create a *int, with any value, this is better than the the builtin new() function, if you are going to assign the value the pointer is pointing to to something other than the default value. It should be noted that,  there are no performance penalty to this because small functions are inlined during compilation.



To summarize i am proposing 2 things:
1) Removal of the new() builtin function
2) Removal of the &t{} construct.

Kamil

unread,
Jun 29, 2014, 4:54:05 AM6/29/14
to golan...@googlegroups.com
Hi,

I understand that this is an old thread but what about first proposal:


1) Removal of the new() builtin function

+

'new(int)'  -> '&int{}'

?

I don't saw in this thread any major objections for this change. So, any plans for that?
I still find this confusing for new people starting with go.

--
Kind Regards, Kamil

Jesse McNelis

unread,
Jun 29, 2014, 8:56:39 PM6/29/14
to Kamil, golang-nuts
On Sun, Jun 29, 2014 at 6:54 PM, Kamil <arv...@gmail.com> wrote:
>> 'new(int)' -> '&int{}'
>
> I don't saw in this thread any major objections for this change. So, any
> plans for that?
> I still find this confusing for new people starting with go.

The major objection is that {} doesn't mean anything for an int.
Why are the braces there?

Dave Cheney

unread,
Jun 29, 2014, 9:08:11 PM6/29/14
to golan...@googlegroups.com
No. Please don't revive a thread that has been dead for two years.

new isn't going away, it can't, it's part of the guaranteed specification for Go 1.

You don't need to use it, most people don't, but that doesn't mean it doesn't have a use.

Kamil Dziedzic

unread,
Jun 30, 2014, 12:07:13 PM6/30/14
to golan...@googlegroups.com
 
Jesse McNelis wrote:
The major objection is that {} doesn't mean anything for an int.
Why are the braces there?

True,

But we are again talking about something different than the topic.

The topic is: having "a := new(t)" and "a := &t{}" is confusing for new people
who think those two expressions do something else while for structs they are the same.

Maybe in other words then, leave "a := new(int)" as this is the only way to get pointer of int,
but remove new for structs. Or any other solution that simplifies this and removes redundancy.

Dave Cheney wrote:
You don't need to use it, most people don't, but that doesn't mean it doesn't have a use.

That's the question, what's the difference between "a := new(t)" and "a := &t{}" ?

http://stackoverflow.com/questions/13244947/is-there-a-difference-between-new-and-regular-allocation

Dave Cheney wrote:
No. Please don't revive a thread that has been dead for two years.

new isn't going away, it can't, it's part of the guaranteed specification for Go 1.

Well, new people didn't stopped coming to Go either. And this isn't less confusing too.
I like the topic "Is anyone working on making Go newbie - friendly?"


Anyway, thanks for response. I understand that some things are difficult to change.
I was just curious if there are any plans to change that as it looked to me that everybody agreed that new(t) is the same as &t{}.

Kind Regards, Kamil

Kevin Gillette

unread,
Jul 1, 2014, 5:56:58 PM7/1/14
to golan...@googlegroups.com
On Monday, June 30, 2014 10:07:13 AM UTC-6, Kamil Dziedzic wrote:
Maybe in other words then, leave "a := new(int)" as this is the only way to get pointer of int,
but remove new for structs. Or any other solution that simplifies this and removes redundancy.

Incorrect. You can also get a pointer to an int using the below approach.

var i int; p := &i 

In this case, eliminating the redundancy would destroy the orthogonality; people would ask: "if I can use new with ints, why can't I use new with structs?"

However pervasive this confusion might be, it's extremely trivial (unimportant), since 10 seconds on the playground is enough to fix any person's misconceptions. If that's insufficient, then it's a remarkably good indicator that the misconceptions run much deeper than just new vs addressed literals.
Reply all
Reply to author
Forward
0 new messages