
Understand Go pointers - spacey
https://dave.cheney.net/2017/04/26/understand-go-pointers-in-less-than-800-words-or-your-money-back
======
agentgt
I am still not sure why the language creators of Go decided to make pointers
explicit (syntactically) instead of making references the default like most
other languages. That is you still have pointers but you don't have to put "*"
all over the place. I suppose it is because the language designers came from C
or maybe they wanted to be that explicit?

I understand the value of having pointers even in a GC but I'am actually more
concerned with the resource I'm pointing to and its lifecycle than that is
pointers. That is there should be different types of pointers depending on
where the data is stored and how it is reclaimed (something Rust does nicely
with generics).

I'm not trying to bash Go rather I must be missing something (I don't know the
language that well).

~~~
echlebek
Having explicit pointers gives you more control over allocation. As you
mention, Go is a GC language, and there are no guarantees of whether
allocation will occur on the stack or the heap. But generally, non-pointer
values will be stack-allocated. This reduces pressure on the GC, since the
values are just discarded along with the stack frame on return.

For this reason, it's usually considered good form to pass values to functions
instead of pointers, even if the values are larger. This allows the compiler
to prove through escape analysis that the arguments don't need to be heap-
allocated. In some cases pointers can be proven not to escape, but in a
limited set of circumstances.

Having explicit pointers also resolves type system awkwardness between simple
and complex data types. Consider Java, which is also a pass-by-value language,
but has "reference" types, except for int, float, etc. So everything is a
reference except this bag of simple data types, effectively creating a
2-tiered type system. Java has dealt with this by implementing boxing and
unboxing rules for int/Integer and friends, which is fine, but adds
complexity.

Finally, one of my personal favourite features about having a complex data
type that is a value instead of a pointer or reference is that you can use
structs and arrays as map keys.

~~~
kristianp
So how big does my struct need to be before its more efficient to pass a
pointer to it to a function?

~~~
Matt3o12_
I've often wondered this myself. Perhaps the right answer would be to
benchmark and/or use the profiler.

On the other handy, I have never really had a situation where I had to pass a
strict that was really big (more then 1MB).

Most of the time, big data structures are actually just slices, which use
pointers to point to the memory address of the underlying array (as far as I
understand).

So having sich big struct is rather unusual. Maybe it is possible to do with
strings, though it would depend on how are strings implemented in go (I would
guess they are just slices as well).

Furthermore, having big strings 1<x<50 MB) is rather unusual unless the
strings can also become really big (1GB in which case you should not probably
pass-by-reference or even better, buffer the string because arbitrary length
strings can easily cause ram problems).

~~~
echlebek
In Go, strings are internally a header that contains a length and a pointer.
The bytes of the string itself are heap allocated.

------
nottorp
I clicked the article thinking that Go pointers are something special... but
this seems targeted at the "programmers" who only know javascript...

~~~
jerf
Since you sort of ask: Go pointers themselves are not that special, because in
a GC'ed language with no pointer arithmetic they're really just a
map/territory-type distinction, but there are some syntax affordances that can
make them briefly more confusing than they appear.

For method calls and field accesses, Go fuzzes whether you have an object or a
pointer by making the "." operator work on both, rather than C's "." vs "->"
distinction. Since it's never ambiguous, it's just pointless overhead to make
the programmer worry about that. It can also be slightly confusing that the
method itself can control whether it receives a pointer, so you can call
object.Method on a concrete object, but Method will receive &object. As a
professional programmer I appreciate not being bothered with this unambiguous
detail that the language can easily handle for me, but it can confuse people
in the early going, which is a legitimate criticism. (I still come down in
favor of doing what it is doing, but it is a legitimate drawback.)

The other thing I see that fools people with experience from other languages
is that the "nil" pointer is not the same thing as a NULL pointer in C. In C,
the NULL pointer is simply a zero with no type information connected to it. In
Go, pointers are actually (type, address) tuples, so a nil pointer to a custom
struct is actually (pointer CustomStruct [1], nil), which means that the
runtime is capable of correctly resolving methods on nil pointers and that you
can therefore write methods that work on nil pointers. Nil pointers are still
bad because they are a value added to all pointer types by the act of taking a
pointer that you can't control, you get it whether you like it or not, _but_
they are not bad in the C sense that any attempt to touch one is a
segfault.[2]

That's about all that matters in normal Go programming.

[1]: Asterisk is confusing HN's italicization there.

[2]: Which is I think an important aspect of understanding the "billion dollar
mistake", by the way; it is easy to deconstruct that mistake as being two
mistakes rolled in to one, which may perhaps explain why it was such a big
mistake.

~~~
pcwalton
> Which is I think an important aspect of understanding the "billion dollar
> mistake", by the way; it is easy to deconstruct that mistake as being two
> mistakes rolled in to one, which may perhaps explain why it was such a big
> mistake.

If it's two mistakes, it's one big one (having null pointers at all) and one
tiny one (the fact that calling methods on null pointers is undefined behavior
in C++). The latter is yet another C++ gotcha, but not nearly as pernicious as
the former, which causes nearly all the null pointer-related bugs in the wild.

~~~
GuB-42
Undefined behavior is what makes C efficient. And NULL pointers are just a
consequence of the ability of pointers to take numerical values. Naturally,
this behavior was kept in C++, which is just a superset of C (with a few minor
exceptions).

NULL pointers and undefined behavior make a lot of sense in C++. The mistake
is replicating this behavior on languages where it doesn't.

~~~
int_19h
> And NULL pointers are just a consequence of the ability of pointers to take
> numerical values.

Not at all. The C standard doesn't allow pointers to take arbitrary numerical
values; while you can cast back and forth, the only guarantee is that casting
a pointer to a numeric value and back to a pointer still points to the
original object (and even then only if the numeric type used is large enough,
which couldn't be portably ensured prior to C99, and is only conditionally
supported even now).

In particular, one thing that the standard does not guarantee - and there have
been implementations that did not do so - is that casting a null pointer to
int will produce zero, or that casting int zero to a pointer will produce
null. Nor does it guarantee that a null pointer is an all-bits-zero value.

The fact that you can write "p = 0" in C is not because pointers can take
arbitrary numerical values, but because the language syntax and semantics
allow you to do so, with 0 being treated specially when assigned to an lvalue
of a pointer type. But you can't write "p = 1", for example, because there's
no such special treatment for any other integer literal.

------
Aardwolf
Looks the same as C pointers. Maybe the title is more clear if it would be:
"Understand pointers, using Go". Then for a C programmer it's clear from the
title that "pointers" is the same concept, and Go doesn't have a different
type of "Go pointers".

Or instead of starting with:

"This post is for programmers coming to Go who are unfamiliar with the idea of
pointers or a pointer type in Go."

It could start with:

"This post is for programmers coming to Go from a language without pointers or
a pointer type."

~~~
echlebek
They're not the same. Go doesn't have pointer arithmetic, and Go functions can
safely return pointers to values that aren't created via new().

[https://play.golang.org/p/m3OdaXH98_](https://play.golang.org/p/m3OdaXH98_)

~~~
pjmlp
Yes it has, via unsafe package.

Which ANSI C example should I write for you in Go?

~~~
echlebek
Package unsafe is in the spec, but converting unsafe.Pointer to uintptr (which
is how I'm supposing you'd do your pointer arithmetic) is implementation-
defined.

This means I could create a perfectly legal implementation of Go where such
things result in complete nonsense. I don't think package unsafe "counts".

~~~
pjmlp
If package unsafe doesn't "count", anything that C compilers allow beyond what
ANSI C specifies don't count as C, thus many of its system programming
features just vanish.

~~~
echlebek
I don't see how that relates to Go pointers.

------
dmix
The article referenced 'Cuneiform' which I had to Google. It's neat to see
that languages evolved the same way math was developed and similar to how
software programs grow as a series of expanding abstraction:

> Emerging in Sumer in the late fourth millennium BC (the Uruk IV period),
> cuneiform writing began as a system of pictograms. In the third millennium,
> the pictorial representations became simplified and more abstract as the
> number of characters in use grew smaller (Hittite cuneiform).

Software is very much a natural extension of the brain and how we processed
the world around us.

~~~
twic
My favourite bit of ancient writing is the Kushim Tablet. It's a clay
document, written in pre-cuneiform archaic Sumerian. It was written in Uruk,
about halfway between Baghdad and Basra in modern-day Iraq, in the 31st
century BC - five thousand years ago. On it is are the oldest examples of two
things fundamental to human civilisation: a person's name, and an industrial
process. It's a receipt for ingredients for a brewery:

[http://www.schoyencollection.com/24-smaller-
collections/wine...](http://www.schoyencollection.com/24-smaller-
collections/wine-beer/ms-1717-beer-inanna-uruk)

------
pavlov
Are there actually programmers who need to be explained the following:
"...memory is just a series of numbered cells, and variables are just
nicknames for a memory location assigned by the compiler."

What kind of programming can one do without even that level of mental model of
what a computer does?

~~~
mikeash
Did you never go through the beginning phase where you didn't understand
pointers? That's usually a breakthrough moment, not something you start out
with.

~~~
pjmlp
No, because the languages I had available to me were Timex 2068 Basic and Z80
Assembly.

Maybe the first couple of hours when I still hadn't read the DIM, DATA, READ,
POKE and PEEK manual pages.

Just like on Dave Cheney's post, seeing a few box examples was enough to get
it.

~~~
mikeash
So you benefitted from exactly the sort of explanation you now seem to be
questioning?

~~~
pjmlp
No, because I never had a "breakthrough moment".

It just felt natural on how a computer was working, typing example after
example, to a 10 year old version of myself.

------
ohstopitu
Thank you! This is extremely useful!

I've always wanted to break into Go, but pointers scare me after my experience
in C in University. While I got the usefulness and it's functions (and usage),
it was not something I felt comfortable with.

This definitely it easier!

------
tapirl
The word "Go" is not essential in the title.

~~~
BoorishBears
If it didn't include Go, this would barely be scratching the surface (pointer
arithmetic)

I see Go's pointer as closer to C#'s ref/out than anything

~~~
jerf
I _personally_ (emphasis intentional) consider the core distinction between
"references" and "pointers" to be whether you can do pointer arithmetic on the
pointers. Pointers without pointer arithmetic aren't hardly scary at all,
especially in languages where there is no way to deallocate the underlying
value but leave the pointer behind such that it may point to the wrong thing
later, be that due to something like Rust or with GC like Go.

So _personally_ , I think of Go as having references, but not pointers
(outside of unsafe).

Of course every language community uses those terms its own way, but that
_seems to me_ to be the most broadly useful way of looking at it.

~~~
steveklabnik
I've spent a lot of time on this, working on Rust's docs, and the way I see it
is that "pointer" is the most generic concept, with "reference" being a more
restricted form. So all references are pointers but not all pointers are
references.

Words are hard.

------
lspears
"Understand Go pointers in less than 800 words or your money back"

There are multiple pictures each of which is worth 1000 words. Where is my
money?

------
sddfd
This is a good explanation of what happens on the machine.

However, most languages (C, C++, etc) have different definitions of what
pointers are, and many operations that seem reasonable on the machine model
are in fact undefined behavior.

In C, for example, you cannot reference one object from a pointer to another
object (there is one exception to this rule).

~~~
weberc2
> In C, for example, you cannot reference one object from a pointer to another
> object (there is one exception to this rule).

I'm confused about what this means--can you explain?

~~~
Arnavion
You can't both cast a double* to an int* and then dereference the int* ,
expecting to read an int-sized chunk of the double. It's called strict
aliasing and there are defined scenarios where it _is_ allowed (one is that
char* is an alias for all pointers).

