Why don't maps allocate on the first attempt to store something in them?
We all know that maps need to be initialized:
var m map[string]string
m["foo"] = bar // panics on STORE
Why can't it allocate ON FIRST STORE instead of panicking?
I DO understand why they aren't allocated initially. The zero value is
nil, not map[T1]T2{}. That makes sense. Allocations should be
explicit.
However, this forces the developer into a situation where they must
either scatter "if m == nil" checks throughout the codebase or force
the allocation at creation time.
For example, in
github.com/DNSControl/dnscontrol every RecordConfig{}
(the struct that stores a DNS record) includes a field Metadata:
map[string]string for storing key/value pairs. Few of the records will
ever store metadata. We don't want to preallocate potentially millions
of empty map structures. However it is annoying to have to check for
nil in the code that does use this feature.
I asked Google, "Are there feature requests in the Go project to
allocate a map on first storage rather than the current behavior which
is to panic?" and got answers that were not very satisfactory:
> Why Go Rejects Auto-Allocation for Maps
> Backwards Incompatibility: Changing nil maps to auto-allocate on write would break existing programs that rely on explicit initialization or specific nil safety checks.
> Performance Intent: Allowing nil maps to exist without allocating memory is an intentional optimization. Developers can declare map variables without incurring heap allocation costs until elements are actually needed.
> Consistency with Other Types: Unlike slices—where a nil slice is functionally identical to a zero-length slice and works with append-maps require an initialized hash-map data structure (hmap pointer) in the runtime before storing entries. Making nil maps auto-initialize would create unique behavior not shared across other reference types.
That's true for auto-allocation at creation, which is not what I'm proposing.
Those arguments apply if you auto-allocate at creation, but that's not
my proposal.
* The existing safety checks would still work because the zero value
is preserved.
* If someone wants to make() with a capacity hint, they can already do that.
* The runtime already does the check to determine if it should panic;
the user-code doing this is redundant.
* If someone needs to carefully control when allocations happen, they
aren't prevented from doing that. Existing code wouldn't break.
* It matches the symmetry for reading from a nil map. (which does not panic)
This seems like a win-win. It would reduce panics and be more
developer-friendly.
Tom