How to sort a slice in Go
On Go 1.21+, slices.Sort(s) sorts any slice of ordered values in place, ascending. For structs or a custom order, use slices.SortFunc(s, func(a, b T) int) and return cmp.Compare(a.Key, b.Key) — swap a and b to sort descending.
Sorting in Go split into a before and an after at version 1.21. Before, everything went through the sort package and its less(i, j int) bool closures; after, the generic slices package gave you Sort, SortFunc, and SortStableFunc, which are shorter, type-safe, and measurably faster. Both still work, and you will meet both in real code. This page covers each one, the stable-vs-unstable distinction that decides multi-key sorts, and the in-place behaviour that surprises people. Each example runs on this page — hit Run, then edit the code and run it again.
1slices.Sort (Go 1.21+)Recommended
slices.Sort(s) sorts in place, ascending, for any element type that supports < — ints, floats, strings. No closure, no imports beyond slices, and nothing to get backwards. There is no descending variant: sort ascending and call slices.Reverse.
Output
Prints [1 2 5 7 9], [apple banana cherry], then [9 7 5 2 1]. Note what it does not return: the slice is reordered in place and slices.Sort returns nothing, so x := slices.Sort(s) is a compile error. Strings sort by byte value, which means every uppercase letter sorts before every lowercase one — "Zebra" lands before "apple". Companion helpers: slices.IsSorted checks without touching the data, and slices.BinarySearch gives you O(log n) lookups once the slice is sorted.
2Structs and custom orders: slices.SortFunc
Real data is rarely a bare []int. slices.SortFunc(s, cmp) takes a three-way comparison — negative if a comes first, zero if they tie, positive if b comes first — and cmp.Compare from the cmp package writes it for you on any ordered field. Reversing the order is then just swapping the two arguments.
Output
Prints [{bob 78000} {carol 95000} {alice 120000}] then the same three in reverse. Prefer cmp.Compare to hand-written if a < b { return -1 } chains: it handles NaN and the equality case correctly and it is one line. Sorting case-insensitively is the same shape — cmp.Compare(strings.ToLower(a), strings.ToLower(b)).
3sort.Slice (any Go version)
Before generics, sorting anything but []int/[]string/[]float64 meant sort.Slice, whose closure takes indices rather than values and returns true when element i should sort before element j. It still works, it is what most pre-1.21 code uses, and it is the only option if you must support old toolchains.
Output
Prints [fig kiwi apple banana] (shortest word first) then [1 2 5 9]. Two costs make slices.SortFunc the better default today: the closure captures the slice variable, so it silently breaks if you reassign words mid-sort, and sort.Slice reaches the elements through reflection, which is why the generic version benchmarks faster. sort.Ints, sort.Strings, and sort.Float64s are formally deprecated as of Go 1.22 in favour of slices.Sort.
4Stable sorts and multi-key ordering
slices.Sort and slices.SortFunc are not stable: elements that compare equal can come out in any order, and the order may change between Go releases. That matters the moment you sort by two keys. Either sort twice with slices.SortStableFunc — least significant key first — or compare both keys in one pass with cmp.Or, which returns its first non-zero argument.
Output
Both lines print [{alice 90} {dana 90} {bob 70} {carol 70}] — highest score first, alphabetical within a score. The one-pass cmp.Or version is the one to reach for: it needs no stability guarantee, it states the whole ordering in one place, and it is a single sort instead of two. Reserve the stable two-pass form for when the existing order is meaningful and you cannot express it as a comparison — data already ordered by arrival time, say.
5Which should you use?
| Form | Needs | Best for |
|---|---|---|
| slices.Sort(s) | Go 1.21+ | Plain ints, floats, strings — the default |
| slices.SortFunc(s, cmp) | Go 1.21+ | Structs, descending, any custom key |
| slices.SortStableFunc(s, cmp) | Go 1.21+ | Ties must keep their existing order |
| sort.Slice(s, less) | Any version | Pre-1.21 codebases |
| slices.Reverse(s) | Go 1.21+ | Flipping an already-sorted slice |
6Sorting mutates — clone when it matters
Every sort in Go reorders the slice you hand it; none returns a new one. Pass a slice to a function that sorts it and the caller's data is sorted too — slices are headers over a shared backing array, so the reordering is visible everywhere that array is reachable. slices.Clone is the fix, and the same aliasing explains why sorting a sub-slice quietly rearranges its parent.
Output
Prints [5 2 9 1], [1 2 5 9], then [2 5 9 1] — the clone left original alone, but sorting the three-element part rewrote the first three elements of original in place. That last line is the whole hazard in one statement. If a function takes a slice and sorts it, document that it does, or clone inside it.
7Sorted map iteration
Go deliberately randomises map iteration order, so ranging a map gives different output on every run — the reason so many Go tests flake on their first day. Maps cannot be sorted; the idiom is to collect the keys into a slice, sort that, and range the keys.
Output
Prints go 124, rust 98, zig 35, then [go rust zig] — in that order, every single run. Pre-allocating with make([]string, 0, len(stars)) avoids regrowing the slice as you append. On Go 1.23+, slices.Sorted(maps.Keys(m)) collapses the whole collect-and-sort dance into one expression. To order by value instead, sort the key slice with slices.SortFunc comparing m[a] against m[b]. Reading values back out is the comma-ok lookup.
Frequently asked questions
How do I sort a slice of ints in Go?
On Go 1.21+, call slices.Sort(nums) — it sorts the slice in place, ascending, and works the same way for float64 and string slices. Older code uses sort.Ints(nums), which is deprecated as of Go 1.22 but still compiles.
How do I sort a slice in descending order in Go?
There is no descending sort function. Either sort ascending and call slices.Reverse(s), or swap the arguments in the comparison: slices.SortFunc(s, func(a, b T) int { return cmp.Compare(b, a) }). The SortFunc form is one pass instead of two.
How do I sort a slice of structs by a field?
Use slices.SortFunc with a three-way comparison on the field you care about: slices.SortFunc(people, func(a, b Person) int { return cmp.Compare(a.Age, b.Age) }). For a second tie-breaker key, wrap both comparisons in cmp.Or, which returns the first non-zero result.
What is the difference between slices.Sort and sort.Slice?
slices.Sort is the generic Go 1.21+ version: type-safe, no closure needed for ordered types, and faster because it does not go through reflection. sort.Slice is the older reflection-based API whose closure compares indices (func(i, j int) bool) rather than values. Both sort in place; prefer the slices package in new code.
Is sorting in Go stable?
No. slices.Sort, slices.SortFunc, and sort.Slice are all unstable — equal elements may be reordered, and the exact result is not guaranteed across Go versions. Use slices.SortStableFunc or sort.SliceStable when the original order of ties must be preserved; both are somewhat slower.
Does sorting a slice modify the original?
Yes. Every sort in the standard library reorders the slice in place and returns nothing, and because a slice is a header over a shared backing array, the caller sees the new order too. Sort slices.Clone(s) instead when the original order still matters.
How do I iterate over a map in sorted order in Go?
Maps have no order — Go randomises iteration on purpose. Collect the keys into a slice, sort it with slices.Sort, then range the sorted keys and look up each value. On Go 1.23+ the same thing is one expression: slices.Sorted(maps.Keys(m)).