-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
slice::sort_by_key has more restrictions than slice::sort_by #34162
Comments
I think this requires HKT (specifically, type parameters of kind |
I was rather afraid of that. |
fn my_sort_by_key<'a, B, F>(&mut self, f: F) where
B: 'a + Ord,
T: 'a,
F: FnMut(&'a T) -> B; Works just fine. But I might've missed some fancy interaction with other code, so maybe it's a breaking change. |
@oli-obk did you try implementing the body of that function?
With your proposed signature, the caller of To do it, we need a way to say something like "there's a lifetime for each call to F: for <'a> FnMut(&'a T) -> (B: Ord + 'a), |
Right, we can get around that with an intermediate trait:
We can even make the intermediate trait unnameable: https://play.rust-lang.org/?gist=1039579366cc210732d3e4672b519370&version=stable |
Can you expand a bit more on what you mean by that? It would be nice if you'd also show an example usage of it. I tried with fn main() {
let mut a = [1,3,2];
a.sort_by_key(|x| x);
a.my_sort_by_key(|x| x);
} And both failed ( |
Perhaps a workaround is to add a |
I stumbled upon very similar issue while trying to sort by |
triage: This is still a thing today, with a slightly better error message: error: lifetime may not live long enough
--> src/main.rs:13:29
|
13 | clients.sort_by_key(|c| c.key());
| -- ^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure is &'2 str
| has type `&'1 Client` |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment has been minimized.
This comment has been minimized.
Now that the necessary HKT properties can be expressed in stable Rust, and have been expressed in the newly released struct Client { key: String, version: u8 }
fn main() {
let clients: &mut [Client] = &mut [];
// Error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
// clients.sort_by_key(|c| &c.key);
// OK
slice_sort_by_key::<HKT!(&str), _, _>(clients, |c| &c.key);
// Important: owned case works too!
slice_sort_by_key::<HKT!(u8), _, _>(clients, |c| c.version);
} Footnotes
|
The following compiles and runs as expected with #![feature(closure_lifetime_binder)]
#![feature(unboxed_closures)]
fn sort_by_key<T, F>(s: &mut [T], mut f: F)
where
F: for<'a> FnMut<(&'a T,)>,
// instead of `B: Ord`
for<'a> <F as FnOnce<(&'a T,)>>::Output: Ord,
{
s.sort_by(|a, b| f(a).cmp(&f(b)))
}
#[derive(Debug)]
struct Client(String);
impl Client {
fn key(&self) -> &str {
&self.0
}
}
fn main() {
let mut test = vec![
Client("c".to_string()),
Client("a".to_string()),
Client("b".to_string()),
];
sort_by_key(&mut test, for<'a> |c: &'a Client| -> &'a str { c.key() });
dbg!(test);
} By eliminating the type parameter |
Here is another way to solve the issue. It is based on previous @Jules-Bertholet's comment, but doesn't require #![feature(closure_lifetime_binder)]
trait ABC<'a, T> {
type B: Ord;
fn ca(&mut self, a: &'a T) -> Self::B;
}
impl<'a, T: 'a, BB: Ord, XYZ> ABC<'a, T> for XYZ
where
XYZ: FnMut(&'a T) -> BB
{
type B = BB;
fn ca(&mut self, a: &'a T) -> BB {
self(a)
}
}
fn sort_by_key<T, F>(s: &mut [T], mut f: F)
where
F: for<'a> ABC<'a, T>,
{
s.sort_by(|a, b| f.ca(a).cmp(&f.ca(b)))
}
#[derive(Debug)]
struct Client(String);
impl Client {
fn key(&self) -> &str {
&self.0
}
}
fn main() {
let mut test = vec![
Client("c".to_string()),
Client("a".to_string()),
Client("b".to_string()),
];
sort_by_key(&mut test, for<'a> |c: &'a Client| -> &'a str { c.key() });
dbg!(test);
} Playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=3bc32cf39a225ca491da94a3c8286e12 |
But I still don't like that two last solutions ( #34162 (comment) and #34162 (comment) ) require too many type annotations ( |
Note that |
Maybe this can get covered by future extension of RTN, and some syntax sugar is even better: #![feature(fn_traits)]
#![feature(unboxed_closures)]
#![feature(return_type_notation)]
#![feature(type_ascription)]
struct Client(String);
impl Client {
fn key(&self) -> &str {
&self.0
}
}
trait Tr<T>
{
fn sort_by_key2<F>(&mut self, f: F)
where
F: for<'a> FnMut<(&'a T,), call_mut(..): Ord>;
// error: return type notation used on function that is not `async` and does not return `impl Trait`
}
impl<T> Tr<T> for Vec<T> {
fn sort_by_key2<F>(&mut self, f: F)
where
F: for<'a> FnMut<(&'a T,), call_mut(..): Ord>
{
todo!();
}
}
fn main() {
let mut clients: Vec<Client> = vec![];
clients.sort_by_key2(|c| c.key());
} |
I expected that these invocations of
sort_by
andsort_by_key
would be equivalent:The implementation of
sort_by_key
:An initial attempt at using HRTB didn't seem to pan out:
The text was updated successfully, but these errors were encountered: