Description
Related PR: #58865
This proposal is about adding a CloneFunc
function to the slices
package.
The signature of the function would be, func CloneFunc[S any, R any](s []S, f func(S) R) []R
.
There are two motivations for such a function.
- Creating a deep copy of a slice
Many structs have something like .Copy
or .Clone
or .DeepCopy
to implement a deep copy mechanism. CloneFunc
enables using the slices
package to make a copy of slices of such structs by leaving the copy implementation up to the caller.
- Creating a conversion of a slice
Often we'll have a struct of one type and wish to create a slice of another type based on the first one. For example, creating a []string
of just the .Name
field from a []Person
. CloneFunc
enables the caller to provide a conversion method when creating the new slice.
I've worked with re-implementations of this function a few places, e.g.
https://github.com/hashicorp/nomad/blob/main/helper/funcs.go#L401-L410
https://github.com/shoenig/test/blob/main/internal/util/slices.go#L3-L10
The function is really about streamlining the monotonous implied for-loop. For example say we wish to deep copy,
type F struct {
M map[string]string
N []string
O []*Object
}
The existing slices
and maps
package enable us to write something like
func (f *F) Copy() *F {
o := make([]*Object, len(f.O))
for i:=0; i<len(f.O); i++ {
o[i] = f.O[i].Copy()
}
return &F{
M: maps.Clone(M),
N: slices.Clone(N),
O: o,
}
}
With the addition of the proposed CloneFunc
, this boils down to
func (f *F) Copy() *F {
return &F{
M: maps.Clone(M),
N: slices.Clone(N),
O: slices.CloneFunc(O, func(o *O) *F { o.Copy() }),
}
}
And for an example of using CloneFunc
to convert a slice, say we have a slice of
type Token struct {
ID string
Age time.Duration
Description string
and we want a slice of just the IDs from the original slice
var result []string
for i:=0; i<len(tokens); i++ {
result[i] = tokens[i].ID
}
With CloneFunc
this becomes
result := slices.CloneFunc(tokens, func(t *Token) string { return t.ID })