Skip to content

proposal: maps: add CloneOrMake function #69469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
callthingsoff opened this issue Sep 14, 2024 · 8 comments
Closed

proposal: maps: add CloneOrMake function #69469

callthingsoff opened this issue Sep 14, 2024 · 8 comments
Labels
Milestone

Comments

@callthingsoff
Copy link
Contributor

callthingsoff commented Sep 14, 2024

Proposal Details

I proposeCloneOrMake function to the maps package.

// CloneOrMake returns a copy of m.  This is a shallow clone:
// the new keys and values are set using ordinary assignment.
//
// CloneOrMake makes a new map and returns it when m is nil.
func CloneOrMake[M ~map[K]V, K comparable, V any](m M) M {
	// Return a non-nil map.
	if m == nil {
		return make(M)
	}
	return clone(m).(M)
}

I found it useful when I need a cloned map which should be non-nil. See recent commit 77e42fd.
If this proposal is accepted, we can omit nil-checks and make the code simpler.

This kind of use case is not rare, another evidence is cloneOrMakeHeader:

go/src/net/http/clone.go

Lines 115 to 121 in 3d33437

func cloneOrMakeHeader(hdr Header) Header {
clone := hdr.Clone()
if clone == nil {
clone = make(Header)
}
return clone
}

@gabyhelp
Copy link

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@gopherbot gopherbot added this to the Proposal milestone Sep 14, 2024
@earthboundkid
Copy link
Contributor

earthboundkid commented Sep 15, 2024

What if it were

package maps

// Init returns a new empty map if m is nil, otherwise it returns m.
func Init[Map ~map[K]V, K comparable, V any](m Map) Map {
    if m == nil {
        return make(M)
    }
    return m
}

If you need a clone, do maps.Init(maps.Clone(m)). Another possible name would be Ensure.

It is fairly common if you write a type that has a private map to just do s.init() at the start of each method, where all init does is make sure m is not nil.

@earthboundkid
Copy link
Contributor

I did a quick search of just golang/go for if \w+ == nil \{\n\s+.+ = make\( and got 91 hits. Some of them are for slices rather than maps, which suggests this could have a package slices equivalent as well. Seems like a pretty frequent thing.

@callthingsoff
Copy link
Contributor Author

I did a quick search of just golang/go for if \w+ == nil \{\n\s+.+ = make\( and got 91 hits. Some of them are for slices rather than maps, which suggests this could have a package slices equivalent as well. Seems like a pretty frequent thing.

Thanks for the search. I think slices are different, usually it needs length or capacity to make in this situation.

@earthboundkid
Copy link
Contributor

For slices, you can do slices.Grow for non-zero length already. Grow doesn't ensure 0 length slices are non-nil, however.

@adonovan
Copy link
Member

Seems like a pretty frequent thing.

Yeah, I just got bit by this last week, but I'm not sure a CloneOrMake function is a solution: if you know about the hazard, you know to call:

m2 := make(M2)
maps.Copy(m2, m1)

and if you don't know about the hazard, you're going to call Clone anyway.

@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Sep 16, 2024
@callthingsoff
Copy link
Contributor Author

76e44f4 and bc7c35a are clean-ups by using maps.Clone.
I'd say it's not easy to do, I have to check the possibility of the new map being written and the old map being nil.

@callthingsoff
Copy link
Contributor Author

What if it were

package maps

// Init returns a new empty map if m is nil, otherwise it returns m.
func Init[Map ~map[K]V, K comparable, V any](m Map) Map {
    if m == nil {
        return make(M)
    }
    return m
}

If you need a clone, do maps.Init(maps.Clone(m)). Another possible name would be Ensure.

It is fairly common if you write a type that has a private map to just do s.init() at the start of each method, where all init does is make sure m is not nil.

I like this idea, closing this because it seems nobody wants a CloneOrMake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants