I'd like to sort a Vec<String>
in ascending or descending order. Here's how I'd do both in a simple example:
use std::cmp::Ordering;
fn main() {
let mut strings = vec!["b".into(), "c".into(), "a".into()];
sort_asc(&mut strings);
assert_eq!(strings, ["a", "b", "c"]); // passes
sort_desc(&mut strings);
assert_eq!(strings, ["c", "b", "a"]); // passes
}
fn sort_asc(strings: &mut Vec<String>) {
strings.sort();
}
fn sort_desc(strings: &mut Vec<String>) {
strings.sort_by(|x, y| {
match x.cmp(y) {
Ordering::Less => Ordering::Greater,
Ordering::Greater => Ordering::Less,
_ => Ordering::Equal,
}
})
}
The sort_asc
function is concise enough that I can just get rid of it and replace it with vec.sort()
wherever I would need it, but the sort_desc
is quite a lot to remember. Ideally I'd like to reduce sort_desc
down to a 1-liner and get rid of it as well. So here's my 1st attempt at that:
use std::cmp::Reverse;
fn sort_desc_2(strings: &mut Vec<String>) { // works
strings.sort_by(|x, y| {
Reverse(x).cmp(&Reverse(y))
})
}
This is slightly better, but not quite there yet. Here's my 2nd attempt:
use std::cmp::Reverse;
fn sort_desc_3(strings: &mut Vec<String>) {
strings.sort_by_key(|x| Reverse(x)) // compile error
}
This is ideal... except it doesn't compile. It throws:
error: lifetime may not live long enough
--> src/main.rs:34:29
|
34 | strings.sort_by_key(|x| Reverse(x))
| -- ^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure is Reverse<&'2 String>
| has type `&'1 String`
This error doesn't make any sense. It doesn't make sense why the input and output lifetimes would be different and it doesn't make sense why the sort_by_key
method would require that in the first place, and in fact it doesn't, this is just a gotcha of Rust: lifetime elision works differently for closures than for regular functions. Thankfully I am familiar with this gotcha and how to fix it, or so I thought, because I tried this and it also didn't work but now with a different error:
fn sort_desc_by_string_key(s: &String) -> Reverse<&String> {
Reverse(s)
}
fn sort_desc_4(strings: &mut Vec<String>) {
strings.sort_by_key(sort_desc_by_string_key) // compile error
}
Now throws:
error[E0308]: mismatched types
--> src/main.rs:42:13
|
42 | strings.sort_by_key(sort_desc_by_string_key)
| ^^^^^^^^^^^ one type is more general than the other
|
= note: expected associated type `<for<'r> fn(&'r String) -> Reverse<&'r String> {sort_desc_by_string_key} as FnOnce<(&String,)>>::Output`
found associated type `<for<'r> fn(&'r String) -> Reverse<&'r String> {sort_desc_by_string_key} as FnOnce<(&String,)>>::Output`
This error also makes no sense. It says it expects $TYPE and it found $TYPE so what's the issue? Of course I could dodge all these errors by cloning but it feels super wasteful because it should be totally unnecessary and this shouldn't be so hard!
fn sort_desc_5(strings: &mut Vec<String>) {
strings.sort_by_key(|x| Reverse(x.clone())) // 🤦
}
Can someone please show me how I can call sort_by_key
on a Vec<String>
without having to clone the String
s? Thank you.