247. sort_unstable — Skip the Allocation When Ties Don't Matter
.sort() pays for a guarantee you usually don’t need: it allocates a scratch buffer to keep equal elements in their original order. .sort_unstable() sorts in place — no allocation, and typically faster.
What “stable” actually buys you
A stable sort preserves the relative order of elements that compare equal. To do that, slice::sort allocates temporary storage proportional to the slice length. slice::sort_unstable works entirely in place and is generally the faster of the two — the docs themselves suggest preferring it when stability isn’t required:
| |
For integers, floats-via-total_cmp, or any type where two equal elements are indistinguishable, stability buys you nothing — there is no observable “original order” among identical values. That’s most sorts you’ll ever write.
When you actually need .sort()
Stability matters when elements carry more data than the sort key, and the order of ties is meaningful. Classic case: re-sorting an already-sorted list by a second criterion:
| |
With sort_unstable_by_key, alice and carol (both dept 2) could end up in either order — the assert above would be a coin flip.
The unstable variants
Everything has an unstable twin: sort_unstable, sort_unstable_by, and sort_unstable_by_key:
| |
Note one asymmetry: sort_by_key with an expensive key function has a cached cousin (sort_by_cached_key, bite 98), but there’s no sort_unstable_by_cached_key — caching needs the same kind of scratch allocation that unstable sorting exists to avoid.
Rule of thumb: reach for sort_unstable by default; upgrade to sort only when a tie-breaking order among equal elements is part of your program’s meaning.