// AppendSeq appends the values from seq to the slice and
// returns the extended slice.
+// If seq is empty, the result preserves the nilness of s.
func AppendSeq[Slice ~[]E, E any](s Slice, seq iter.Seq[E]) Slice {
for v := range seq {
s = append(s, v)
}
// Collect collects values from seq into a new slice and returns it.
+// If seq is empty, the result is nil.
func Collect[E any](seq iter.Seq[E]) []E {
return AppendSeq([]E(nil), seq)
}
// Sorted collects values from seq into a new slice, sorts the slice,
// and returns it.
+// If seq is empty, the result is nil.
func Sorted[E cmp.Ordered](seq iter.Seq[E]) []E {
s := Collect(seq)
Sort(s)
// SortedFunc collects values from seq into a new slice, sorts the slice
// using the comparison function, and returns it.
+// If seq is empty, the result is nil.
func SortedFunc[E any](seq iter.Seq[E], cmp func(E, E) int) []E {
s := Collect(seq)
SortFunc(s, cmp)
// It then sorts the slice while keeping the original order of equal elements,
// using the comparison function to compare elements.
// It returns the new slice.
+// If seq is empty, the result is nil.
func SortedStableFunc[E any](seq iter.Seq[E], cmp func(E, E) int) []E {
s := Collect(seq)
SortStableFunc(s, cmp)
// and, if i < len(s), r[i+len(v)] == value originally at r[i].
// Insert panics if i > len(s).
// This function is O(len(s) + len(v)).
+// If the result is empty, it has the same nilness as s.
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
_ = s[i:] // bounds check
// Delete is O(len(s)-i), so if many items must be deleted, it is better to
// make a single call deleting them all together than to delete one at a time.
// Delete zeroes the elements s[len(s)-(j-i):len(s)].
+// If the result is empty, it has the same nilness as s.
func Delete[S ~[]E, E any](s S, i, j int) S {
_ = s[i:j:len(s)] // bounds check
// DeleteFunc removes any elements from s for which del returns true,
// returning the modified slice.
// DeleteFunc zeroes the elements between the new length and the original length.
+// If the result is empty, it has the same nilness as s.
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
i := IndexFunc(s, del)
if i == -1 {
// modified slice.
// Replace panics if j > len(s) or s[i:j] is not a valid slice of s.
// When len(v) < (j-i), Replace zeroes the elements between the new length and the original length.
+// If the result is empty, it has the same nilness as s.
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
_ = s[i:j] // bounds check
// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
// The result may have additional unused capacity.
+// The result preserves the nilness of s.
func Clone[S ~[]E, E any](s S) S {
// Preserve nilness in case it matters.
if s == nil {
// Compact modifies the contents of the slice s and returns the modified slice,
// which may have a smaller length.
// Compact zeroes the elements between the new length and the original length.
+// The result preserves the nilness of s.
func Compact[S ~[]E, E comparable](s S) S {
if len(s) < 2 {
return s
// CompactFunc is like [Compact] but uses an equality function to compare elements.
// For runs of elements that compare equal, CompactFunc keeps the first one.
// CompactFunc zeroes the elements between the new length and the original length.
+// The result preserves the nilness of s.
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
if len(s) < 2 {
return s
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow panics.
+// The result preserves the nilness of s.
func Grow[S ~[]E, E any](s S, n int) S {
if n < 0 {
panic("cannot be negative")
}
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
+// The result preserves the nilness of s.
func Clip[S ~[]E, E any](s S) S {
return s[:len(s):len(s)]
}
}
// Concat returns a new slice concatenating the passed in slices.
+// If the concatenation is empty, the result is nil.
func Concat[S ~[]E, E any](slices ...S) S {
size := 0
for _, s := range slices {
t.Error("clone keeps alive s due to array overlap")
}
}
+
+// This test asserts the behavior when the primary slice operand is nil.
+//
+// Some operations preserve the nilness of their operand while others
+// do not, but in all cases the behavior is documented.
+func TestNilness(t *testing.T) {
+ var (
+ emptySlice = []int{}
+ nilSlice = []int(nil)
+ emptySeq = func(yield func(int) bool) {}
+ truth = func(int) bool { return true }
+ equal = func(x, y int) bool { panic("unreachable") }
+ )
+
+ wantNil := func(slice []int, cond string) {
+ if slice != nil {
+ t.Errorf("%s != nil", cond)
+ }
+ }
+ wantNonNil := func(slice []int, cond string) {
+ if slice == nil {
+ t.Errorf("%s == nil", cond)
+ }
+ }
+
+ // The update functions
+ // Insert, AppendSeq, Delete, DeleteFunc, Clone, Compact, CompactFunc
+ // preserve nilness, like s[i:j].
+ wantNil(AppendSeq(nilSlice, emptySeq), "AppendSeq(nil, empty)")
+ wantNonNil(AppendSeq(emptySlice, emptySeq), "AppendSeq(nil, empty)")
+
+ wantNil(Insert(nilSlice, 0), "Insert(nil, 0)")
+ wantNonNil(Insert(emptySlice, 0), "Insert(empty, 0)")
+
+ wantNil(Delete(nilSlice, 0, 0), "Delete(nil, 0, 0)")
+ wantNonNil(Delete(emptySlice, 0, 0), "Delete(empty, 0, 0)")
+ wantNonNil(Delete([]int{1}, 0, 1), "Delete([]int{1}, 0, 1)")
+
+ wantNil(DeleteFunc(nilSlice, truth), "DeleteFunc(nil, f)")
+ wantNonNil(DeleteFunc(emptySlice, truth), "DeleteFunc(empty, f)")
+ wantNonNil(DeleteFunc([]int{1}, truth), "DeleteFunc([]int{1}, truth)")
+
+ wantNil(Replace(nilSlice, 0, 0), "Replace(nil, 0, 0)")
+ wantNonNil(Replace(emptySlice, 0, 0), "Replace(empty, 0, 0)")
+ wantNonNil(Replace([]int{1}, 0, 1), "Replace([]int{1}, 0, 1)")
+
+ wantNil(Clone(nilSlice), "Clone(nil)")
+ wantNonNil(Clone(emptySlice), "Clone(empty)")
+
+ wantNil(Compact(nilSlice), "Compact(nil)")
+ wantNonNil(Compact(emptySlice), "Compact(empty)")
+
+ wantNil(CompactFunc(nilSlice, equal), "CompactFunc(nil)")
+ wantNonNil(CompactFunc(emptySlice, equal), "CompactFunc(empty)")
+
+ wantNil(Grow(nilSlice, 0), "Grow(nil, 0)")
+ wantNonNil(Grow(emptySlice, 0), "Grow(empty, 0)")
+
+ wantNil(Clip(nilSlice), "Clip(nil)")
+ wantNonNil(Clip(emptySlice), "Clip(empty)")
+ wantNonNil(Clip([]int{1}[:0:0]), "Clip([]int{1}[:0:0])")
+
+ // Concat returns nil iff the result is empty.
+ // This is an unfortunate irregularity.
+ wantNil(Concat(nilSlice, emptySlice, nilSlice, emptySlice), "Concat(nil, ...empty...)")
+ wantNil(Concat(emptySlice, emptySlice, nilSlice, emptySlice), "Concat(empty, ...empty...)")
+ wantNil(Concat[[]int](), "Concat()")
+
+ // Repeat never returns nil. Another irregularity.
+ wantNonNil(Repeat(nilSlice, 0), "Repeat(nil, 0)")
+ wantNonNil(Repeat(emptySlice, 0), "Repeat(empty, 0)")
+ wantNonNil(Repeat(nilSlice, 2), "Repeat(nil, 2)")
+ wantNonNil(Repeat(emptySlice, 2), "Repeat(empty, 2)")
+
+ // The collection functions
+ // Collect, Sorted, SortedFunc, SortedStableFunc
+ // return nil given an empty sequence.
+ wantNil(Collect(emptySeq), "Collect(empty)")
+
+ wantNil(Sorted(emptySeq), "Sorted(empty)")
+ wantNil(SortedFunc(emptySeq, cmp.Compare), "SortedFunc(empty)")
+ wantNil(SortedStableFunc(emptySeq, cmp.Compare), "SortedStableFunc(empty)")
+}