Difference between var, new and :=

2,451 views
Skip to first unread message

Alexander Demin

unread,
Apr 18, 2012, 8:13:12 AM4/18/12
to golan...@googlegroups.com
Hi,

Please consider the code:

package main
type A struct {
  i int
  s string
  m map[string]string
  c chan bool
}
func main() {
  var a A
  println(a.i, a.s, a.m, a.c)
  b := A{}
  println(b.i, b.s, b.m, b.c)
  c := *new(A)
  println(c.i, c.s, c.m, c.c)
}

It prints:

Hello, playground
0  0x0 0x0
0  0x0 0x0
0  0x0 0x0

What's the difference between the declarations of "a", "b", "c"? Do they do allocate in different places - stack or heap? For me, they all do the same thing declaring a value variable, so it isn't obvious to pick the "proper" one.

Which one is the ideomatic and (mainly) why?

Regards,
Alexander



Christoph Hack

unread,
Apr 18, 2012, 9:28:56 AM4/18/12
to golan...@googlegroups.com
  var a A
  println(a.i, a.s, a.m, a.c)
  b := A{}
  println(b.i, b.s, b.m, b.c)

Both variables, a and b will have the type A. The later one just uses the short variable declaration syntax and is the normally the preferred way, at least inside functions / methods. But both are fine.
 
  c := *new(A)
  println(c.i, c.s, c.m, c.c)

Here, c will have the type *A, which is a pointer to A. Those types are distinct, but when you access an attribute of c, the Go compiler will automatically dereference the pointer first. Alternatively, and a bit more idomatic is it to write "d := &A{}". Here, d will also be a *A.
 
Do they do allocate in different places - stack or heap? For me, they all do the same thing declaring a value variable, so it isn't obvious to pick the "proper" one.

If the Go compiler can prove that the pointer won't escape (i.e. that it is neither send over a channel or used as return value), it will allocate it on the stack. This minimizes the work of the garbage collector drastically. On the other hand, if you take a pointer of a variable on the stack and return it, this value will be moved to the heap automatically.

-christoph

Peter Bourgon

unread,
Apr 18, 2012, 9:30:40 AM4/18/12
to Alexander Demin, golan...@googlegroups.com
>   var a A
>   b := A{}
>   c := *new(A)

>
> What's the difference between the declarations of "a", "b", "c"?

Effectively nothing.

> Do they do allocate in different places - stack or heap?

"Stack" or "heap" allocation is abstracted away from the programmer in Go.

> Which one is the ideomatic and (mainly) why?

The end result of all of these operations is a variable (a, b or c)
which represents an "A" structure by value, with no explicit
initialization, ie. all members of "A" with their default values. The
'b' form is probably the most idiomatic, because it can easily accept
explicit initialization:

b := A{1, "abc"}

or be transformed to referring to an "A" structure by reference:

b := &A{}

A case can be made for the 'a' form in some circumstances. The 'c'
form is a bit circuitous. (I've not used the "new" keyword in a long,
long time.)

Jim Whitehead II

unread,
Apr 18, 2012, 9:32:51 AM4/18/12
to Christoph Hack, golan...@googlegroups.com
On Wed, Apr 18, 2012 at 2:28 PM, Christoph Hack <tux...@gmail.com> wrote:
>>   var a A
>>   println(a.i, a.s, a.m, a.c)
>>   b := A{}
>>   println(b.i, b.s, b.m, b.c)
>
>
> Both variables, a and b will have the type A. The later one just uses the
> short variable declaration syntax and is the normally the preferred way, at
> least inside functions / methods. But both are fine.
>
>>
>>   c := *new(A)
>>   println(c.i, c.s, c.m, c.c)
>
>
> Here, c will have the type *A, which is a pointer to A. Those types are
> distinct, but when you access an attribute of c, the Go compiler will
> automatically dereference the pointer first. Alternatively, and a bit more
> idomatic is it to write "d := &A{}". Here, d will also be a *A.

Not to nit-pick, but c here has the type A, since it has been
dereferenced explicitly, no?

- Jim

Jan Mercl

unread,
Apr 18, 2012, 9:34:46 AM4/18/12
to golan...@googlegroups.com
On Wednesday, April 18, 2012 3:28:56 PM UTC+2, Christoph Hack wrote:
  c := *new(A)
  println(c.i, c.s, c.m, c.c)

Here, c will have the type *A, which is a pointer to A. Those types are distinct, but when you access an attribute of c, the Go compiler will automatically dereference the pointer first. Alternatively, and a bit more idomatic is it to write "d := &A{}". Here, d will also be a *A.

No. new() returns *A, then it's dereferenced, thus c is of type A: http://play.golang.org/p/nWU1fXXiq6

It also means, that this specific way of a variable short declaration makes little sense.

Christoph Hack

unread,
Apr 18, 2012, 9:37:31 AM4/18/12
to golan...@googlegroups.com, Christoph Hack


On Wednesday, April 18, 2012 3:32:51 PM UTC+2, Jim Whitehead wrote:

Not to nit-pick, but c here has the type A, since it has been
dereferenced explicitly, no?

Oh, sorry. I have overlooked the small asterisk. Thanks for your correction :)

Alexander Demin

unread,
Apr 18, 2012, 9:40:18 AM4/18/12
to golan...@googlegroups.com, Alexander Demin
Yes, you've spot on exactly my initiation intention why I'd posted the question.

I'm struggling to figure out the purpose of "new". "var" and ":=" are understandable depending whether you want to initilize or not, and in the libraries they usually construct compound objects by "&Typename{initializers}".

When you see a few ways possible, you start thinking that there is a purpose for it.

Alexander  

Jim Whitehead II

unread,
Apr 18, 2012, 9:58:30 AM4/18/12
to Alexander Demin, golan...@googlegroups.com
On Wed, Apr 18, 2012 at 2:40 PM, Alexander Demin <ade...@gmail.com> wrote:
> Yes, you've spot on exactly my initiation intention why I'd posted the
> question.
>
> I'm struggling to figure out the purpose of "new". "var" and ":=" are
> understandable depending whether you want to initilize or not, and in the
> libraries they usually construct compound objects by
> "&Typename{initializers}".
>
> When you see a few ways possible, you start thinking that there is a purpose
> for it.

Personally, I use := when I just am doing a simple assignment,
function call, etc. I think that I only use var when I'm pre-declaring
something that will be used in a later block. Since its not being
initialized, I have to make sure the type is specified, and it helps
to make that bit of code a bit more clear.

As for composite literal &Typename{} versus new(Typename), I use the
former when I am completely initializing a new object that I am
creating, and the latter when the relationship between my objects
means that that will be an in-between state where the new Typename is
not fully set up until I've done something else. For example:

// Two objects that refer to each other, could do this:
foo := &Foo{
bar: &Bar{}
}
bar := foo.bar
bar.foo = foo

But I'd probably do that this way instead:

foo := new(Foo)
bar := new(Bar)
foo.bar = bar
bar.foo = foo

Contrived, but those are the (kind of) guidelines that I use.

- Jim

Matt Kane's Brain

unread,
Apr 18, 2012, 10:10:43 AM4/18/12
to Alexander Demin, golan...@googlegroups.com
On Wed, Apr 18, 2012 at 09:40, Alexander Demin <ade...@gmail.com> wrote:
> When you see a few ways possible, you start thinking that there is a purpose
> for it.

Using the ":=" operator doesn't work for package-level variables. Also
if you want a zero value for a particular type. I think "var i int16"
is clearer than "i := int16(0)".

The ":=" operator also has a bonus if you want to declare a single new
variable to accept one out of multiple return values:

func whatsit() (foo []byte, err error) {
f, err := os.Open("filename") // assigns to existing "err"
if err != nil { return }
foo, err = ioutil.ReadAll(f)
return
}

--
matt kane's brain
http://hydrogenproject.com

Reply all
Reply to author
Forward
0 new messages