The slices package
You already met slices.Delete, slices.Equal, and slices.Contains in the last lesson. The rest of the slices package is the same shape: small generic helpers that replace a decade of hand-rolled loops. This lesson is a tour of the ones you are most likely to reach for first.
The package landed in Go 1.21. Every function is generic, so they work on []int, []string, []MyType, and so on without conversions.
The functions you reach for
slices.Contains(s, v) // true if s has v
slices.Index(s, v) // first index of v, or -1
slices.Sort(s) // in place, ascending
slices.SortFunc(s, func(a, b T) int { ... }) // in place with custom order
slices.Equal(a, b) // element-by-element equality
slices.Clone(s) // shallow copy (new backing array)
slices.Delete(s, i, j) // remove s[i:j], returns new len
slices.Insert(s, i, v...) // insert at i, returns new slice
slices.Compact(s) // collapse consecutive duplicates
That is most of them. Each one would have been a five-line loop before generics.
Sort and Compact: the dedupe pattern
Compact only collapses consecutive duplicates, so the standard "remove duplicates" pattern is sort-then-compact:
nums := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}
slices.Sort(nums) // [1 1 2 3 3 4 5 5 6 9]
nums = slices.Compact(nums) // [1 2 3 4 5 6 9]
Both functions modify in place, but Compact may shrink the slice, so you must reassign.
SortFunc: custom orderings
SortFunc takes a comparator that returns negative, zero, or positive (the cmp.Compare convention from Go 1.21):
import "cmp"
type Person struct {
Name string
Age int
}
people := []Person{{"Bo", 30}, {"Ada", 25}, {"Cy", 28}}
slices.SortFunc(people, func(a, b Person) int {
return cmp.Compare(a.Age, b.Age) // ascending by age
})
This is not the older func(a, b T) bool "less" form from sort.Slice. The modern API uses three-way compare: negative means a sorts before b, positive means after, zero means equal.
For descending, swap the arguments: cmp.Compare(b.Age, a.Age).
Insert and Delete
s := []int{1, 2, 3, 4}
s = slices.Insert(s, 1, 99) // [1 99 2 3 4]
s = slices.Delete(s, 0, 2) // [2 3 4] (removes s[0:2])
Delete takes a half-open range [i, j). To remove a single element, call slices.Delete(s, i, i+1).
Both return the new slice. They may share the underlying array with the input, so always reassign.
Equal and Clone
slices.Equal([]int{1, 2}, []int{1, 2}) // true
slices.Equal([]int{1, 2}, []int{1, 3}) // false
c := slices.Clone(s) // shallow copy with a fresh backing array
Equal walks the two slices once and compares element-by-element. Clone is the safe "I need to mutate without affecting the caller's slice" copy. Both are O(n).
What about the sort package?
The classic sort.Sort, sort.Slice, sort.Search still exist and you will see them all over older code. They still work; the Standard library chapter covers them. For new code, reach for slices first. It is shorter, generic, and usually the clearer default.
slices.SortFunc is not stable: equal elements may be reordered. If you need to preserve the relative order of equal items (sorting an already-grouped list by a secondary key, for example), reach for slices.SortStableFunc.
The challenge on the right walks you through a small slices workout: sort, compact, contains, index, insert, delete. Run it locally with go run . and compare your output to the expected block.