Step 5 of 7
Sorting
sort() puts a vector in ascending order, in place. reverse() flips the order.
fn main() {
let mut scores = vec![88, 42, 97, 61, 42];
scores.sort();
println!("{:?}", scores);
scores.reverse();
println!("{:?}", scores);
let mut names = vec!["mika", "Ana", "bo", "Zed"];
names.sort();
println!("{:?}", names);
names.sort_by_key(|name| name.to_lowercase());
println!("{:?}", names);
}
[42, 42, 61, 88, 97]
[97, 88, 61, 42, 42]
["Ana", "Zed", "bo", "mika"]
["Ana", "bo", "mika", "Zed"]
Strings sort by their character codes, and every uppercase letter comes before every lowercase one, so "Zed" lands before "bo". To sort ignoring case, sort_by_key sorts by a key computed from each element.
|name| name.to_lowercase() is a closure: a small function without a name, written right where it's used. Between the bars are its parameters; after them comes the value it returns. Here: "given a name, the key is its lowercase version". Module 26 covers closures in depth; for sorting, this shape is all you need.
Custom orders with sort_by
sort_by takes a closure that compares two elements and returns an Ordering (Less, Equal or Greater). a.cmp(&b) produces one:
fn main() {
let mut results = vec![("bo", 72), ("ana", 91), ("dee", 72), ("cy", 85)];
results.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(b.0)));
for (name, score) in &results {
println!("{name}: {score}");
}
let mut lap_times: Vec<f64> = vec![61.2, 59.8, 60.5];
lap_times.sort_by(|a, b| a.total_cmp(b));
println!("{:?}", lap_times);
}
ana: 91
cy: 85
bo: 72
dee: 72
[59.8, 60.5, 61.2]
b.1.cmp(&a.1)compares the scores backwards (b before a), which sorts from highest to lowest..then(...)breaks ties: if the scores are equal, compare the names normally. That's how leaderboards, reports and tests get a fully predictable order.total_cmpis how you sort floats (see the next section).
Stable and unstable
sort, sort_by and sort_by_key are stable: elements that compare equal keep their original order. sort_unstable (and sort_unstable_by, sort_unstable_by_key) may reorder equal elements but is a little faster and needs no extra memory. For plain numbers you can't tell equal values apart, so sort_unstable is a good default there.
Common mistakes
Sorting floats with
sort():fn main() { let mut temps: Vec<f64> = vec![21.5, 19.0, 23.25]; temps.sort(); println!("{:?}", temps); }error[E0277]: the trait bound `f64: Ord` is not satisfiedsortneeds a total order (Ord): every pair of values must compare as less, equal or greater. Floats haveNaN("not a number"), which isn't even equal to itself, sof64isn'tOrd. Usetemps.sort_by(|a, b| a.total_cmp(b)), which defines an order for every float, NaN included.Expecting a sorted copy.
sortsorts in place and returns nothing:let sorted = v.sort();gives you(). To keep the original, copy it first (let mut sorted = v.clone();or, from a slice,v.to_vec()) and sort the copy.
Your task
Write fn top_three(scores: &[u32]) -> Vec<u32> that returns the three highest scores, highest first (fewer if there are fewer than three). You can't sort through a shared &[u32] borrow, so start with let mut sorted = scores.to_vec();, which copies it into a new vector you own.
Challenge 2
Write fn sort_by_length(words: &mut [String]) that sorts words from shortest to longest, with words of the same length in alphabetical order. Use sort_by with .then(...), or use the fact that sorting is stable: sort alphabetically first, then sort_by_key(|w| w.len()).
Challenge 3
Write a program that reads race times in seconds (one per line, like 9.58) and prints them fastest first, numbered, with two decimals:
1. 9.58
2. 9.69
3. 9.72
Remember that f64 can't use sort().
Previous: Insert, remove, swap, truncate, clear Next: dedup, retain, contains and binary_search