Last Updated: May 22, 2026
Go 1.21 added a standard-library package called slices that ships with ready-made versions of the operations we kept hand-rolling in earlier lessons. Sorting, searching, deleting at an index, inserting in the middle, comparing two slices for equality, finding a min or a max: each of these is now a single call. The previous lesson on slice tricks showed the manual append(s[:i], s[i+1:]...) pattern for delete and the equivalent two-step append dance for insert. From Go 1.21 onward, prefer slices.Delete and slices.Insert for those jobs. The import path is just import "slices", and the package lives in the standard library, no module dependency required.
The slices.Sort function sorts any slice whose element type satisfies the cmp.Ordered constraint, meaning the built-in ordered types: integers, floats, and strings. It sorts in place, using the < operator internally. There's no need to pass a comparator for the common case.
slices.Sort mutates the original slice, it doesn't return a new one. The original order is gone after the call. If you need to keep the unsorted copy, clone the slice first with slices.Clone (covered later in this lesson).
slices.Sort runs in Θ(n log n) time. It uses a pattern-defeating quicksort that handles already-sorted, reverse-sorted, and partially-sorted input well, but the worst case is still n log n.
For anything that isn't one of the ordered built-ins, like a slice of structs, use slices.SortFunc. You pass a comparison function that returns a negative number when a should come before b, zero when they're equal, and a positive number when a should come after b. The cmp package provides cmp.Compare for ordered types, which makes the common case a one-liner.
The comparator returns an int, not a bool. This matches the cmp.Compare convention and lets you express richer orderings without writing branches yourself. To sort descending, swap the operands: cmp.Compare(b.Price, a.Price).
slices.SortFunc is not stable: two products with the same price could end up in either order. When the relative order of equal elements matters, use slices.SortStableFunc. It guarantees that elements judged equal by the comparator stay in their original order. The cost is slightly more memory and a small constant-factor slowdown, but the API is identical.
The three products priced at $4.50 come out in the order they went in: Notebook, Pen, Stickers. With slices.SortFunc, their relative order would be unspecified.
slices.IsSorted answers a different question: is the slice already sorted? It returns a bool and runs in O(n). It's useful as a precondition check before calling a function like slices.BinarySearch, which requires sorted input.
There's also slices.IsSortedFunc for custom comparators, which mirrors slices.SortFunc exactly.
For unsorted slices, slices.Contains does a linear scan and returns whether the value is present.
slices.Index returns the index of the first match, or -1 when the value isn't found. It scans the slice the same way Contains does, just returning the position instead of a bool.
When the element type doesn't support == (struct types with non-comparable fields, or any case where equality means "matches a predicate"), use the Func variants. slices.ContainsFunc and slices.IndexFunc both take a predicate func(T) bool and return as soon as the predicate is true.
slices.Contains, slices.Index, and their Func variants are all O(n). They stop at the first match, but in the worst case they touch every element.
For sorted slices, slices.BinarySearch is the standard tool. It runs in O(log n) and returns two values: the index where the target was found, and a bool reporting whether the match was exact. When the target isn't present, the returned index is the position where it could be inserted to keep the slice sorted.
The 15.00 lookup returns index 2 because that's where 15.00 would slot in: between 7.50 (index 1) and 19.99 (index 2). For 99.99, the returned index is 5, the length of the slice, which means "append to the end". This insertion-point property pairs nicely with slices.Insert to keep a sorted slice sorted as new items arrive.
slices.BinarySearch is O(log n) but the slice must already be sorted in ascending order. On an unsorted slice the result is meaningless. Use slices.IsSorted as a guard if you aren't sure.
slices.BinarySearchFunc takes a comparator and lets you binary-search a slice of structs by some field.
The comparator's first argument is an element of the slice, the second is the target. The slice has to be sorted by the same key the comparator uses (here, Price), otherwise the search returns nonsense.
slices.Equal checks whether two slices have the same length and the same elements in the same order. It uses == on each element, so the element type must be comparable.
Order matters: cart1 and cart3 hold the same three items but in different positions, so slices.Equal returns false. For unordered equality (set semantics), sort both first or use a map.
slices.EqualFunc swaps == for a custom equality predicate, useful when comparing structs by a subset of their fields or doing case-insensitive comparisons.
slices.Compare gives a three-way result: it returns -1, 0, or +1 based on a lexicographic comparison of two slices. Element by element, it compares pairs in order; the first difference decides the result. If one slice is a prefix of the other, the shorter one comes first.
Read those four cases line by line. Identical slices return 0. The second pair differs at index 2 (3 < 4), so the result is -1. The third pair compares equal through index 1 but the left slice is shorter, so it's "less than". The fourth pair differs at index 0 (2 > 1), and the comparator stops there even though the right slice is longer.
slices.Min and slices.Max return the smallest and largest element of an ordered slice. They both run in O(n).
Calling either function on an empty slice panics. Check the length first, or use the Func variants if you need custom ordering.
For a slice of structs, slices.MinFunc and slices.MaxFunc take a comparator with the same signature as slices.SortFunc.
slices.Delete removes a range of elements from a slice and returns the shortened result. It replaces the manual append(s[:i], s[j:]...) form. The function signature is Delete(s []T, i, j int) []T, where i is inclusive and j is exclusive, the same convention as slicing expressions.
Indices 1 and 2 (Pen and USB Cable) are removed. To delete a single element at index i, call slices.Delete(cart, i, i+1).
Reassign the return value. slices.Delete does shift elements in place, but the resulting slice has a shorter length. Without the assignment, your slice variable still has the old length and will show stale elements at the tail.
slices.Delete from index i is O(n - i) because the elements to the right of the removed range have to slide left. It also zeros the trailing elements of the underlying array so the garbage collector can reclaim pointers they held. The next lesson on slice gotchas covers why that zeroing matters.
slices.Insert is the mirror operation: it places one or more values into the slice at a given index and returns the new slice.
Pen and Stickers are inserted starting at index 1, pushing USB Cable and Headphones to the right. Like Delete, you must reassign the return value, and like any operation that grows a slice, it may allocate a new backing array if the capacity isn't enough.
slices.Concat joins two or more slices into one new slice. It always returns a fresh allocation, so the inputs are never modified.
slices.Concat is variadic, it takes any number of slices. The original slices are untouched.
slices.Clone returns a shallow copy of a slice. The new slice has its own backing array, so writes to one no longer touch the other.
The clone is shallow. If the element type contains pointers, slices, or maps, those are shared between the original and the clone. For deep copies of struct slices, you have to walk the elements manually.
slices.Reverse reverses a slice in place. It runs in O(n) and uses constant extra memory.
Like slices.Sort, slices.Reverse returns nothing. It mutates the slice you pass in. If you need a reversed copy without losing the original, clone first: reversed := slices.Clone(cart); slices.Reverse(reversed).
The table below lists the functions covered in this lesson with their complexity and any preconditions.
| Function | Purpose | Complexity | Notes |
|---|---|---|---|
slices.Sort | Sort ordered slice in place | O(n log n) | Element type must satisfy cmp.Ordered |
slices.SortFunc | Sort with custom comparator | O(n log n) | Comparator returns int, not bool. Not stable |
slices.SortStableFunc | Stable sort with comparator | O(n log n) | Preserves order of equal elements |
slices.IsSorted | Check if slice is sorted | O(n) | Pair with BinarySearch as a guard |
slices.Contains | Does value exist? | O(n) | Linear scan |
slices.ContainsFunc | Does any element match predicate? | O(n) | Stops at first match |
slices.Index | Index of first match | O(n) | Returns -1 if not found |
slices.IndexFunc | Index of first predicate match | O(n) | Returns -1 if not found |
slices.BinarySearch | Find in sorted slice | O(log n) | Slice must be sorted ascending |
slices.BinarySearchFunc | Binary search with comparator | O(log n) | Slice must be sorted by comparator key |
slices.Equal | Are two slices element-wise equal? | O(n) | Same length and same elements in order |
slices.EqualFunc | Equality with custom predicate | O(n) | For non-comparable types or fuzzy match |
slices.Compare | Lexicographic three-way compare | O(n) | Returns -1, 0, or +1 |
slices.Min | Smallest element | O(n) | Panics on empty slice |
slices.Max | Largest element | O(n) | Panics on empty slice |
slices.Delete | Remove range [i:j) | O(n - i) | Reassign return value. Zeros trailing slots |
slices.Insert | Insert values at index i | O(n - i) | May allocate if capacity is exceeded |
slices.Concat | Join multiple slices | O(total length) | Always allocates a new slice |
slices.Clone | Shallow copy | O(n) | New backing array, shared pointee values |
slices.Reverse | Reverse in place | O(n) | Constant extra memory |