blog@​arhan.sh:~$

Rust's Sharpest Corner is... sort_by_key?

!! UNPUBLISHED !! • 2 min read • more posts


Rust tends to surprise me in the most unexpected ways. A few weeks ago I was working on something that roughly looked like this.

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Data {
    x: Box<u8>,
}

fn sort_in_reverse(data_list: &mut [Data]) {
    data_list.sort_unstable_by_key(|data| std::cmp::Reverse(data));
}

This is so simple; what could possibly be wrong here? Let’s try to compile it!

error: lifetime may not live long enough
 --> src/lib.rs:7:43
  |
7 |     data_list.sort_unstable_by_key(|data| std::cmp::Reverse(data));
  |                                     ----- ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
  |                                     |   |
  |                                     |   return type of closure is Reverse<&'2 Data>
  |                                     has type `&'1 Data`

Okay, what? I’ve been programming Rust for a few years, and I thought I was familiar with the language, but this error left me scratching my head. The culprit turns out to be the function signature for sort_unstable_by_key in the standard library.

pub fn sort_by_key<K, F>(&mut self, f: F)
where
    F: FnMut(&T) -> K,
    K: Ord,

The function call monomorphizes T as Data, K as Reverse<&'2 Data>, and F as impl FnMut(&'1 Data) -> Reverse<&'2 Data>. Without some form of post-monomorphization analysis, the borrow checker requires FnMut(&'1 T) -> K to produce a valid K no matter how short-lived '1 is

The root cause should now be clear: the borrow checker is unable to unify the lifetimes of the two references of F together.

Sure. But isn’t this what for bounds are for?

pub fn sort_by_key<T, B, F>(this: &mut [T], mut f: F)
where
    for<'a> F: FnMut(&'a T) -> B + 'a,
    B: Ord,
{
    unimplemented!()
}


#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Data {
    x: Box<u8>,
}

fn sort_in_reverse(data_list: &mut [Data]) {
    sort_by_key(data_list, |data| std::cmp::Reverse(data));
}

Unfortunately, the compiler gives us the exact same error.

A bit of poking around, and I found a remedy.

data_list.sort_unstable_by_key(|data| std::cmp::Reverse(data));
data_list.sort_unstable_by(|data1, data2| data2.cmp(data1));

But I still really wanted to

↑ Scroll to top ↑