slice::sort_by¶
Level: reference · for working programmers
One line: Sort in place by a comparison closure you write — descending order, floats, or several keys at once.
Stable since 1.0.0.
The closure gets two references and returns an Ordering ↗. |a, b| a.cmp(b) is what sort does; swap them, |a, b| b.cmp(a), and the slice sorts descending. Stable, like sort.
Three jobs it does that sort cannot:
- Floats.
f64is notOrd.a.partial_cmp(b).unwrap()works until aNaNarrives, then panics;f64::total_cmpnever does, and it is a plain function so it passes without a closure:floats.sort_by(f64::total_cmp). - Several keys. Chain with
then_with↗:a.len().cmp(&b.len()).then_with(|| a.cmp(b))sorts by length, then alphabetically among equal lengths. - A field.
|a, b| a.0.cmp(&b.0)— though when the comparison is only a key,sort_by_keysays so more clearly.
The comparison must be a total order — consistent with itself across every pair — or the sort may panic. A closure that returns Ordering::Equal for NaN and something else for everything else breaks that, which is the second reason to prefer total_cmp.
Example¶
slice_sort_by.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
use std::cmp::Ordering;
fn main() {
let mut nums = vec![5, 3, 9, 1];
nums.sort_by(|a, b| b.cmp(a));
println!("descending: {nums:?}");
// Two keys: by length, then alphabetically among equal lengths.
let mut words = vec!["pear", "fig", "apple", "kiwi", "date"];
words.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)));
println!("by length, then name: {words:?}");
// Longest first, still alphabetical within a length: reverse only the first key.
words.sort_by(|a, b| match a.len().cmp(&b.len()) {
Ordering::Equal => a.cmp(b),
other => other.reverse(),
});
println!("longest first: {words:?}");
// f64 has no Ord. partial_cmp().unwrap() works until a NaN arrives.
let mut floats = vec![2.5, -1.0, 0.0];
floats.sort_by(|a, b| a.partial_cmp(b).unwrap());
println!("{floats:?}");
let mut with_nan = vec![2.5, f64::NAN, -1.0];
with_nan.sort_by(f64::total_cmp);
println!("{with_nan:?} <- total_cmp puts NaN last, and never panics");
// The closure sees references, so a field comparison is a.0.cmp(&b.0).
let mut pairs = vec![(3, "c"), (1, "a"), (2, "b")];
pairs.sort_by(|a, b| a.0.cmp(&b.0));
println!("{pairs:?}");
}
Verified output of slice_sort_by.rs — regenerated by tools/run_examples.py, never hand-typed.
descending: [9, 5, 3, 1]
by length, then name: ["fig", "date", "kiwi", "pear", "apple"]
longest first: ["apple", "date", "kiwi", "pear", "fig"]
[-1.0, 0.0, 2.5]
[-1.0, 2.5, NaN] <- total_cmp puts NaN last, and never panics
[(1, "a"), (2, "b"), (3, "c")]
See also¶
slice::sort— the default comparisonslice::sort_by_key— when the comparison is only a keyslice::sort_unstable— the no-allocation variant;sort_unstable_bytakes the same closure