Re: Should structs always be passed by reference?

8,868 views
Skip to first unread message

peterGo

unread,
Mar 19, 2013, 12:38:03 PM3/19/13
to golan...@googlegroups.com
Jakub,

Structs can be passed by value if they are small and inexpensive to copy. For example the time.Time struct:

"Programs using times should typically store and pass them as values, not pointers. That is, time variables and struct fields should be of type time.Time, not *time.Time."

type Time
http://golang.org/pkg/time/#Time

Peter

On Tuesday, March 19, 2013 11:02:36 AM UTC-4, Jakub Arnold wrote:
I guess this is a very broad question, but since I'm a beginner, I can't really specify this much more.

I've looked at some code and libraries, for example redigo, and what I'm mostly seeing is that structs are always passed by reference and interfaces by value. From what I've understood, the interface is passed by value because it already contains a pointer to the underlying struct, which is why there's no need to have it passed by reference.

Is there a good rule of thumb for when I should consider passing something by value, rather than by reference?

Rob Pike

unread,
Mar 19, 2013, 12:56:08 PM3/19/13
to peterGo, golan...@googlegroups.com
In Go, everything is passed by value. Everything.

There are some types (pointers, channels, maps, slices) that have
reference-like properties, but in those cases the relevant data
structure (pointer, channel pointer, map header, slice header) holds a
pointer to an underlying, shared object (pointed-to thing, channel
descriptor, hash table, array); the data structure itself is passed by
value. Always.

Always.

-rob

John Nagle

unread,
Mar 19, 2013, 1:18:35 PM3/19/13
to golan...@googlegroups.com
On 3/19/2013 9:56 AM, Rob Pike wrote:
> In Go, everything is passed by value. Everything.

One can say that of all programming languages. At
the bottom, something is passed by value. But sometimes
it's a pointer or a reference that goes on the stack
or in a register. That's generally considered to be
a pass by reference. See

http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference

> There are some types (pointers, channels, maps, slices) that have
> reference-like properties, but in those cases the relevant data
> structure (pointer, channel pointer, map header, slice header) holds
> a pointer to an underlying, shared object (pointed-to thing, channel
> descriptor, hash table, array); the data structure itself is passed
> by value. Always.
>
> Always.

From Wikipedia:

"in the Java community, they say that Java is pass-by-value, whereas in
the Ruby community, they say that Ruby is pass-by-reference, even though
the two languages exhibit the same semantics."

From "Through the Looking Glass":

"When I use a word," Humpty Dumpty said, in rather a scornful tone, "it
means just what I choose it to mean�neither more nor less."
"The question is," said Alice, "whether you can make words mean so many
different things."
"The question is," said Humpty Dumpty, "which is to be master,
that's all."

John Nagle

Andrew Gerrand

unread,
Mar 19, 2013, 1:33:58 PM3/19/13
to John Nagle, golang-nuts

On 19 March 2013 10:18, John Nagle <na...@animats.com> wrote:
"in the Java community, they say that Java is pass-by-value, whereas in
the Ruby community, they say that Ruby is pass-by-reference, even though
the two languages exhibit the same semantics."

Regardless of the confusion within those communities, what Rob says is correct. There is no "pass by reference" in Go as there arguably is in Java and Ruby.

Andrew

Andrew Gerrand

unread,
Mar 19, 2013, 1:42:55 PM3/19/13
to alan....@gmail.com, John Nagle, golang-nuts

On 19 March 2013 10:39, Alan Strohm <alan....@gmail.com> wrote:
On the original question, should the assumption be that structs are light weight? It sounds like the only things that could make a struct expensive to copy are: arrays, strings (maybe? if they're immutable do they have to be copied?), and a lot of fields.  It seems like structs should call themselves out if they are heavy and the assumption should be that they are light-weight.

The answer is always "it depends". If your struct has 20 fields then it's not "light weight" compared to a pointer. 


If in doubt, measure and compare.

Andrew

John Nagle

unread,
Mar 19, 2013, 1:52:09 PM3/19/13
to golan...@googlegroups.com
To get away from the manipulable term "pass by reference", it may be
useful to speak in terms of "shared by caller and callee". In Go,
slice targets, maps, and channels are shared between caller and callee.

The Go FAQ is clear on this, but the language specification is less
clear. Is there anything in the spec that would prohibit an
implementation from making a copy of a map when a map was passed
to a function?

For immutable types, you can't tell if they're shared.

John Nagle



Maxim Khitrov

unread,
Mar 19, 2013, 1:56:25 PM3/19/13
to John Nagle, golan...@googlegroups.com
On Tue, Mar 19, 2013 at 1:52 PM, John Nagle <na...@animats.com> wrote:
> The Go FAQ is clear on this, but the language specification is less
> clear. Is there anything in the spec that would prohibit an
> implementation from making a copy of a map when a map was passed
> to a function?

I think that's implied by "Slices, maps and channels are reference types..."

- Max

Rob Pike

unread,
Mar 19, 2013, 2:04:19 PM3/19/13
to Maxim Khitrov, John Nagle, golan...@googlegroups.com
I have been deleting appearances of the term 'reference types' from
the official documents, since the community has found the term
misleading. It's also incorrect in the strict sense.

-rob

Rob Pike

unread,
Mar 19, 2013, 2:07:04 PM3/19/13
to John Nagle, golan...@googlegroups.com
The spec is indeed unclear on the topic of map values. I will file an issue.

-rob

John Nagle

unread,
Mar 19, 2013, 2:13:57 PM3/19/13
to Rob Pike, Maxim Khitrov, golan...@googlegroups.com
On 3/19/2013 10:56 AM, Maxim Khitrov wrote::
>>(Nagle) The Go FAQ is clear on this, but the language specification is
less
>> clear. Is there anything in the spec that would prohibit an
>> implementation from making a copy of a map when a map was passed
>> to a function?
>
> I think that's implied by "Slices, maps and channels are reference
types..."
>
> - Max

The language specification says that only in the context of the
semantics of "make", not the semantics of parameter passing:

"Making slices, maps and channels

Slices, maps and channels are reference types that do not require the
extra indirection of an allocation with new."

That's the only use of the term "reference type" in the document.

That does not strictly imply that an entire map object should
not be copied when assigned or passed.

Slices, maps, and channels are
really object types. Go has objects; you just can't create new
classes.
They could be called "objects", perhaps.

A table in the spec which shows, for each built-in type,
what gets copied on assignment or parameter pass would be
helpful.


John Nagle


Rob Pike

unread,
Mar 19, 2013, 2:23:17 PM3/19/13
to na...@animats.com, Maxim Khitrov, golan...@googlegroups.com
The documentation issue is number 5083. The global issue that people
don't understand pointers, values, and parameter passing in
programming languages requires a higher level of issue management.

-rob

Kevin Gillette

unread,
Mar 19, 2013, 4:37:34 PM3/19/13
to golan...@googlegroups.com, na...@animats.com, Maxim Khitrov
I've been thinking of writing a few articles for the wiki:
  • "Go for Python Programmers", parts of which I may generalize into a "Go Concepts from a Dynamic Typing Perspective" article.
  • A general "Pointers and Reference Types" article for programmers entirely unfamiliar with pointers, stressing the semantics (in contrast and with counters to the argument I've been hearing lately: "use pointers anywhere you want performance").
  • A shorter "Go for C Programmers", which extracts pertinent points out of and would be referenced by the C++ wiki article.
If the wiki supports directory-style organization, it'd probably be good to put all these "Go for XYZ Programmers" in a separate group from the other pages.

If this sounds like something the community would want, just let me know what I'd need to do for wiki inclusion.

Nate Finch

unread,
Mar 19, 2013, 4:56:02 PM3/19/13
to golan...@googlegroups.com
Getting back on topic:

I assume you mean "pass by pointer" since there's no actual concept of a reference in Go.  As others said, it's always pass by value, it's just that some things (like slices, maps, and interfaces) have "small" values, which include a pointer to other data that might be "large".

So... when do you use a pointer to a struct instead of just passing the struct itself? It depends, but there's some guidelines. If you need to edit the contents of a struct, you'll need to pass a pointer (or return the edited contents and assign it back to the original struct).

As for size-wise.... I presume a pointer is 32 bits on a 32 bit machine and 64 on a 64 bit machine, but it's actually not spelled out in the spec. Anyway, if the struct is significantly bigger than the size of a pointer, then you probably want to pass around a pointer.  The size of a struct is equal to the size of the values in that struct.

That being said, dereferencing a pointer takes more than zero time, so if it's only a little bigger than the size of a pointer, you might want to pass around the struct by value... it also makes APIs a little cleaner not to always need to use a pointer, check if it's nil, etc.

Passing a type into a function that takes an interface copies the type... but then the interface just holds a pointer to that data, so they can be passed around without using a pointer, since they're not much bigger than a pointer.

Alan Strohm

unread,
Mar 19, 2013, 1:39:23 PM3/19/13
to Andrew Gerrand, John Nagle, golang-nuts
On the original question, should the assumption be that structs are light weight? It sounds like the only things that could make a struct expensive to copy are: arrays, strings (maybe? if they're immutable do they have to be copied?), and a lot of fields.  It seems like structs should call themselves out if they are heavy and the assumption should be that they are light-weight.

--Alan


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rob Pike

unread,
Mar 19, 2013, 6:20:41 PM3/19/13
to alan....@gmail.com, Andrew Gerrand, John Nagle, golang-nuts
You should use a pointer when a pointer is appropriate. The reasoning
involves semantics first and efficiency second. Big things are slower
than small things. Indirection has a cost. Tradeoffs exist.

-rob

Thomas Bushnell, BSG

unread,
Mar 19, 2013, 6:43:26 PM3/19/13
to Rob Pike, Maxim Khitrov, John Nagle, golang-nuts
Thanks! The relevant point with slices, it seems to me, is that the data in the underlying array is shared when a slice is passed around, but the bounds are not.


Kevin Gillette

unread,
Mar 19, 2013, 11:11:20 PM3/19/13
to golan...@googlegroups.com
On Tuesday, March 19, 2013 2:56:02 PM UTC-6, Nate Finch wrote:
As for size-wise.... I presume a pointer is 32 bits on a 32 bit machine and 64 on a 64 bit machine, but it's actually not spelled out in the spec.

In Go, pointers are uintptr sized.  Nothing beyond that would be spelled out in the spec, since it's platform dependent.

Certainly contemporary mainstream platforms have native word sized addressing instructions, but that can't be held as a universal assumption. For example, networked/cloud computing may be so pervasive for all processing if and when mainstream 128-bit processors come along, that there'd be little benefit to large amounts of local memory -- in these cases, even though the processor has a native word size of 128 bits, addressing instructions may still be limited to 64 bits (since 64-bits of real addressing already gets into the exbibyte range -- way more than enough for thin-client needs in the foreseeable future). It wouldn't make sense to carry around 128-bit pointers when those hypothetical platforms top out in the multi-terabyte range of ram. Even today, most or all amd64 compatible processors do not have 64-bits of real address space -- they're processor-limited to something smaller; in the case of my emt64 intel atom netbook, 32-bits of physical address space (even if the motherboard supported larger than 4gb of ram, my cpu wouldn't), and 48-bits virtual (for mmu remapping onto that 32-bits of physical addressable space), so a large range of addresses are simply invalid.
Reply all
Reply to author
Forward
0 new messages