First impressions using go.

14 views
Skip to first unread message

german diago

unread,
Nov 13, 2009, 7:37:21 AM11/13/09
to golang-nuts
Hello. I'm using go to see if I get it. I wrote some other posts, but
I have more feedback and, as always, some doubts.

This piece of (legal) code looks strange to me, and it's confusing:

func f() *Person {
var Person p;
p.Name = "SomeName";
return &p
}

I think that if some function returns a pointer to p, it should
ALLOCATE the result in a pointer and it should be illegal with an
error message saying that returning a pointer to a p variable
allocated in the stack is illegal.
So that piece of code would become illegal and it would be substituted
by:

func f() *Person {
p := &Person{"SomeName"}
return p
}

Which doesn't make you to wonder how all the magic in the first
function happened (at least from a C/C++ point of view)

Another thing I would like to see (if possible, and if it fits in the
language) is some kind of Properties a la C#. I mean, assigning to a
field that triggers a function to check if correct. This make the
interface clearer from my point of view.

Is there any way to put default arguments in structs?

I don't understand very well the difference between make and new.
Which are the differences?
Thanks for your time.

Peter Bourgon

unread,
Nov 13, 2009, 8:34:22 AM11/13/09
to german diago, golang-nuts
It's also unclear to me how the compiler can "know" that when I return
&p from a function, it should heap-allocate the object. I mean, I
guess I can see "how", I just don't understand the "why" part. I would
find a compiler error in

> func f() *Person {
> var Person p;
> p.Name = "SomeName";
> return &p
> }

much more comfortable. But, maybe there is some documentation
explaining this behavior that I've overlooked?

Rob 'Commander' Pike

unread,
Nov 13, 2009, 11:06:46 AM11/13/09
to peter....@gmail.com, german diago, golang-nuts

On Nov 13, 2009, at 5:34 AM, Peter Bourgon wrote:

It's also unclear to me how the compiler can "know" that when I return
&p from a function, it should heap-allocate the object. I mean, I
guess I can see "how", I just don't understand the "why" part. I would
find a compiler error in

func f() *Person {
var Person p;
p.Name = "SomeName";
return &p
}

much more comfortable. But, maybe there is some documentation
explaining this behavior that I've overlooked?

I don't think there's enough about "why" in the documentation, but in this case it seems a reasonable thing to do.  Your function f() creates new Person structs and returns their address.  Why should that be an error?

It was important to us in designing Go that we tried to eliminate a priori many of the common bug patterns in C.  This is certainly one of the big ones.  It's nice that what used to be a bug became a useful idiom.

-rob

Ian Lance Taylor

unread,
Nov 13, 2009, 11:57:15 AM11/13/09
to german diago, golang-nuts
german diago <germa...@gmail.com> writes:

> This piece of (legal) code looks strange to me, and it's confusing:
>
> func f() *Person {
> var Person p;
> p.Name = "SomeName";
> return &p
> }
>
> I think that if some function returns a pointer to p, it should
> ALLOCATE the result in a pointer and it should be illegal with an
> error message saying that returning a pointer to a p variable
> allocated in the stack is illegal.

Since the compiler sees you return the address of a local variable, it
creates the variable on the heap. That seems more useful than making
this invalid.

I think this is only confusing if you approach Go as being the same as
C.


> Another thing I would like to see (if possible, and if it fits in the
> language) is some kind of Properties a la C#. I mean, assigning to a
> field that triggers a function to check if correct. This make the
> interface clearer from my point of view.

We don't have a feature like that.


> Is there any way to put default arguments in structs?

No.


> I don't understand very well the difference between make and new.
> Which are the differences?

make creates a map, channel, or slice. new allocates memory in the
heap. new(map[int]int) gives you a pointer to a map[int]int, but the
map will be nil. To create the map, you need to use make (or a map
composite literal). It would be perfectly reasonable to do
p := new(map[int]int);
*p = make(map[int]int);

Ian

Robert Griesemer

unread,
Nov 13, 2009, 1:40:05 PM11/13/09
to peter....@gmail.com, german diago, golang-nuts
It is *crucial* that you don't have to worry about where variables are allocated; programming in a style where you are forced to allocate on the heap just because a pointer is escaping the function is simply too cumbersome. This becomes even more evident when using closures. For instance, in:

package main


// function f returns a function

func f(x int) (func() int) {

return func() int {

return x

}

}


func main() {

g := f(10);

println(g());

}


the function f returns a closure, an anonymous function that captures the parameter of x. The returned function (assigned to g in main) can later be called and one gets the value of x back. In this case, x is automatically allocated on the heap (since it is a parameter for f, it is copied into the heap upon activation of f) as it must survive the activation of f. If you had to change your program here to make x look like a pointer and copy it into your heap yourself (because the compiler gave you an error message as you'd like), using closures would become very cumbersome. Not having closures is a non-starter for a modern programming language.

The compiler effort to detect these situations is very well understood and not very hard either.

Another point along these lines: If you declare a local array in a function, say var a [1000000]int, it is probably allocated on the heap even if it doesn't escape the function and no address is ever taken. Why? Because otherwise your activation frame would become huge (> 4MB in this case). Finally, since the Go implementation (6g for now, gccgo forthcoming) is using segmented stacks (which are simply allocated in the heap), the entire discussion regarding stack vs heap allocation is somewhat moot.

A consequence of the implicit heap allocation is that

- new(T) is the same as &T{}, if T is a composite literal
- you could write a function myNew() { var t T; return &t; } and get the same effect as new(T) (and this is a reasonable way to write a "constructor")
- you never have to worry about taking the address of a local variable, or that it might escape
- programming has become a whole lot safer and easier

- gri

german diago

unread,
Nov 13, 2009, 2:23:33 PM11/13/09
to golang-nuts


On 13 nov, 19:40, Robert Griesemer <g...@golang.org> wrote:
> It is *crucial* that you don't have to worry about where variables are
> allocated; programming in a style where you are forced to allocate on the
> heap just because a pointer is escaping the function is simply too
> cumbersome. This becomes even more evident when using closures. For
> instance, in:

You convinced me. If there is garbage collection, there is no need to
be explicit
about the stack or the heap. This is reasonable. Maybe it's my mind
which
plays tricks on me because I'm thinking of C/C++/C# all the time,
which make
this explicit.

> Another point along these lines: If you declare a local array in a function,
> say var a [1000000]int, it is probably allocated on the heap

Also logical, of course.


> A consequence of the implicit heap allocation is that
>
> - new(T) is the same as &T{}, if T is a composite literal
> - you could write a function myNew() { var t T; return &t; } and get the
> same effect as new(T) (and this is a reasonable way to write a
> "constructor")
> - you never have to worry about taking the address of a local variable, or
> that it might escape
> - programming has become a whole lot safer and easier
>

Ok. I just need some time to get my head around these facts. But it's
ok I think.

german diago

unread,
Nov 13, 2009, 2:33:18 PM11/13/09
to golang-nuts


> make creates a map, channel, or slice.  new allocates memory in the
> heap.  new(map[int]int) gives you a pointer to a map[int]int, but the
> map will be nil.  

I don't understand this very well. How do you reserve a pointer to a
map, but
the map is nil? I mean, when the memory is reserved, shouldn't this
type be fully
usable? I'm confused.


To create the map, you need to use make (or a map
> composite literal).  It would be perfectly reasonable to do
>         p := new(map[int]int);
>         *p = make(map[int]int);

new(map[int]int allocates memory without initializing the map?
and make initializes the map itself? I don't know very well what
happens there, but I'd like to know.
The only thing I know is that channels, slices and maps are reference
types, am I right?

John Cowan

unread,
Nov 13, 2009, 2:44:12 PM11/13/09
to german diago, golang-nuts
german diago scripsit:

> > make creates a map, channel, or slice. �new allocates memory in the
> > heap. �new(map[int]int) gives you a pointer to a map[int]int, but the
> > map will be nil. �
>
> I don't understand this very well. How do you reserve a pointer to a
> map, but the map is nil? I mean, when the memory is reserved, shouldn't
> this type be fully usable? I'm confused.

Calling "new(map[int]int)" returns a null pointer with a static type
of *map[int]int. Nothing is allocated.

> To create the map, you need to use make (or a map
> > composite literal). �It would be perfectly reasonable to do
> > � � � � p := new(map[int]int);

This is the same as "p *map[int]int = nil;"

> > � � � � *p = make(map[int]int);
>
> new(map[int]int allocates memory without initializing the map?
> and make initializes the map itself? I don't know very well what
> happens there, but I'd like to know.

No. make allocates and constructs the map, and the second assignment
(beginning "*p = ") causes p to point to that map.

--
John Cowan co...@ccil.org http://ccil.org/~cowan
I am he that buries his friends alive and drowns them and draws them
alive again from the water. I came from the end of a bag, but no bag
went over me. I am the friend of bears and the guest of eagles. I am
Ringwinner and Luckwearer; and I am Barrel-rider. --Bilbo to Smaug

Marcin 'Qrczak' Kowalczyk

unread,
Nov 13, 2009, 2:50:30 PM11/13/09
to german diago, golang-nuts
2009/11/13 german diago <germa...@gmail.com>:

> I mean, when the memory is reserved, shouldn't this
> type be fully usable? I'm confused.

It is not the case in Go. An object of any type can be zero
initialized, but whether such a zero initialized object is in a valid
and useful state depends on the particular type.

An unfortunate consequence is that it is tempting to rely on zero
initialization for types like simple structs, which will break if the
types evolve into something more elaborate which needs nontrivial
initialization of some fields.

In general a type needs an explicit constructor function if it does
not want its clients to depend on the fact whether its zero
representation is a useful state. In case of a few builtin types the
magic make "function" serves as a constructor function.

The constructor function can return either an object or a pointer to
an object depending on whether the type is a copyable value type or a
type of objects with meaningful identity. Map[k]v is formally a value
type but the value is merely a pointer-like handle to a real map which
is not exposed directly, so copying the handle is valid but does not
copy the map itself.

(I hope I am right here, I have not really used Go.)

--
Marcin Kowalczyk

Marcin 'Qrczak' Kowalczyk

unread,
Nov 13, 2009, 2:52:39 PM11/13/09
to John Cowan, german diago, golang-nuts
2009/11/13 John Cowan <co...@ccil.org>:

> Calling "new(map[int]int)" returns a null pointer with a static type
> of *map[int]int.  Nothing is allocated.

Really? I would expect that it returns a pointer to an allocated
"null" map value... If I am wrong then I am really confused.

--
Marcin Kowalczyk

Russ Cox

unread,
Nov 13, 2009, 4:13:01 PM11/13/09
to Marcin 'Qrczak' Kowalczyk, John Cowan, german diago, golang-nuts
You're right: new(map[int]int) != nil.

Russ

Peter Bourgon

unread,
Nov 13, 2009, 4:55:51 PM11/13/09
to John Cowan, german diago, golang-nuts
> Calling "new(map[int]int)" returns a null pointer with a static type
> of *map[int]int. Nothing is allocated.

Like Marcin, I am confused by this statement as well. By my
understanding, this should allocate the space but simply not
initialize it.

Peter Bourgon

unread,
Nov 13, 2009, 4:58:23 PM11/13/09
to John Cowan, german diago, golang-nuts
Ach, should have updated and caught Ross' reply before posting my own.
Apologies.
Reply all
Reply to author
Forward
0 new messages