Skip to content

spec: add clear(x) builtin, to clear map, zero content of slice #56351

Closed
@rsc

Description

@rsc

There is no way to clear a map in Go. You can write

for k := range m {
    delete(m, k)
}

but that only works if m does not contain any key values that contain NaNs.

Based on the discussion in #55002, we suggest adding delete(m) to the language to clear the map, always (even if it contains NaNs).

This is somewhat a reopening of #45328, which we closed because the loop was good enough, but we missed the earlier discussion in that issue of NaNs.

Adding delete(m) would then let us also add a similar mechanism in reflect.
We wouldn't need to add maps.Clear(m) since delete(m) is just as good.

Activity

moved this to Incoming in Proposalson Oct 20, 2022
added this to the Proposal milestone on Oct 20, 2022
rsc

rsc commented on Oct 20, 2022

@rsc
ContributorAuthor

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

moved this from Incoming to Active in Proposalson Oct 20, 2022
atdiar

atdiar commented on Oct 20, 2022

@atdiar

Personally, I'm ok with it but can't determine if it is good enough.

Having a way to disallow NaN containing values in maps would be best. Then map clearing via iteration just works and the change in this proposal is unnecessary (if not for perf reasons).

Otherwise, comparing two maps may still have edge cases. (the crux of the issue is that comparison is actually underspecified it seems).
Notably relevant for Set types that would be implemented by using maps.

With this proposal, map clearing gets solved but not inadvertent comparison of potential NaN composites.
If deemed a serious issue, I'm a bit concerned that it could actually hide a programming mistake instead.

Other than that, this proposal should be semantically sound at least.

robpike

robpike commented on Oct 20, 2022

@robpike
Contributor

I tend to agree that this is really only about NaNs, and is a peculiar response to their presence. At least, if you want to justify a new predefined function with a new capability, making NaNs the selling point is off-key (see what I did there?).

earthboundkid

earthboundkid commented on Oct 20, 2022

@earthboundkid
Contributor

I worry that this could lead to programmer error when clearing map[string]map[string]T or map[string]any.

timothy-king

timothy-king commented on Oct 20, 2022

@timothy-king
Contributor

With delete(m), will it be clear whether this is releasing memory of the underlying map?

  • If it does release the memory, it is equivalent to if m != nil { m = make(m[K]V) }. (But maybe I am not thinking through aliasing enough?) [edit: I was not thinking through aliasing enough.]
  • If it does not release the memory, will users assume that it does and misuse it? My guess is yes. So if this goes forward with this semantics, maybe document that in the spec?
bcmills

bcmills commented on Oct 20, 2022

@bcmills
Contributor

@atdiar

If deemed a serious issue, I'm a bit concerned that it could actually hide a programming mistake instead.

I think this proposal strictly improves the situation for irreflexive keys: if you use it it does the right thing for clearing the map, and if you don't use it you're no worse off than if it never existed.

That said, this still wouldn't help use-cases that need to add and remove individual keys: delete(m) would only give an all-or-nothing approach, where in order to delete an irreflexive key you must burn the rest of the map to the ground along with it.


The programming mistake I'm more worried about is typos. If both delete(m, k) and delete(m) are defined, then I could inadvertently write delete(m) when I meant delete(m, k), and they're visually similar enough that the bug could be difficult to spot. 😅

randall77

randall77 commented on Oct 20, 2022

@randall77
Contributor

With delete(m), will it be clear whether this is releasing memory of the underlying map?

I would assume this does not release the memory, just as the delete-with-a-for-loop doesn't.

If we ever implement #20135 the map will shrink eventually if it is never refilled.

Merovius

Merovius commented on Oct 20, 2022

@Merovius
Contributor

@carlmjohnson To be clear, what's the programming error? Both of these would work fine ISTM. For the first I can see that perhaps a programmer might've thought it deletes the inner maps (though that seems a bit far-fetched to me). But the second?

@timothy-king

If it does release the memory, it is equivalent to if m != nil { m = make(m[K]V) }. (But maybe I am not thinking through aliasing enough?)

You are not. func Clear[K , V](m map[K, V]) { m = make(m[K]V) } is a NOP, but func Clear[K, V](m map[K, V]) { delete(m) } is not.

If it does not release the memory, will users assume that it does and misuse it? My guess is yes.

What's the misuse? Semantics don't change. I am opposed to specify allocation behavior. That's an optimization that's up to implementers.

148 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @josharian@rsc@magical@earthboundkid@cespare

      Issue actions

        spec: add clear(x) builtin, to clear map, zero content of slice · Issue #56351 · golang/go