Skip to content

proposal: slices: add a CloneFunc function #58881

Closed as not planned
Closed as not planned
@shoenig

Description

@shoenig

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.

  1. 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.

  1. 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 })

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions