zero maps rationale

116 views
Skip to first unread message

Amnon

unread,
Oct 7, 2020, 3:05:39 AM10/7/20
to golang-nuts
Go generally ensures that zero values are useful.

So a zero slice allows us to append to it, without having to do any special initialization.

This approach also extends to the standard library.

sync.Mutexs are unlocked and usable in the zero state.

Even zero sync.Maps ready for use.

So why are maps different? Why is it necessary to make a map, before it can be assigned to?
Is there any use case where a zero map which can not be assigned to is useful?

Axel Wagner

unread,
Oct 7, 2020, 3:16:53 AM10/7/20
to Amnon, golang-nuts
Hi,

this has been discussed many times, e.g.
In short: The semantics of maps require them to be pointer-shaped. Currently, all zero values are a sequence of zero bytes (making it very cheap to initialize to them). That means the zero value of maps can't be made useful in that manner. It's unfortunate, but seems necessary.

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/14e25f79-8fab-4cec-8c00-fedd26488014n%40googlegroups.com.

Brian Candler

unread,
Oct 7, 2020, 4:46:58 AM10/7/20
to golang-nuts
On Wednesday, 7 October 2020 08:05:39 UTC+1, Amnon wrote:
Go generally ensures that zero values are useful.

So a zero slice allows us to append to it, without having to do any special initialization.


Not exactly.  Appending to a slice always creates a *new* slice value:

    s2 := append(s1, v)

(Aside: the new slice may or may not share the same backing store as the original slice, depending on whether s1's backing is suitable or not. But s2 is always a new *value*)

It becomes clear when you realise that a slice value is really a hidden struct, in pseudo-code something like:

struct {
    backing *T
    cap     int
    len     int
}

The zero slice value is this struct initialized to all zeros.  This gives a null pointer (i.e. no backing store), and a zero cap and len.

There's very little you can do with this value, except determine that its length is zero.

So why are maps different? Why is it necessary to make a map, before it can be assigned to?
Is there any use case where a zero map which can not be assigned to is useful?

Just like the zero slice, the zero map can be read from, and it returns len() of zero.  Reading any key from it will return zero value.

Like the slice, the map is also a struct which contains a pointer. However for simplicity and efficiency reasons, you add to a map by mutating it, not by creating a new map.  This means that the map must have been initialized, so that its internal pointer points to a valid data structure, before you can update it.

If you want appending to a map to work the same way as appending to a slice, you can wrap it yourself easily enough:

Reply all
Reply to author
Forward
0 new messages