为什么我不能使用在使用sort_by_key排序向量时返回引用的键函数?

时间:2017-11-05 13:13:42

标签: generics rust lifetime

我正在尝试使用键函数对Vec<String>进行排序,该函数返回对向量中字符串的引用。一个人为的例子是使用身份函数作为关键函数(当然这是无用的,但它是重现我的问题的最小例子):

fn key(x: &String) -> &String {
    x
}

现在给items: Vec<String>,我希望能够做到

items.sort_by_key(key);

这会出现以下错误:

error[E0271]: type mismatch resolving `for<'r> <fn(&std::string::String) -> &std::string::String {main::key} as std::ops::FnOnce<(&'r std::string::String,)>>::Output == _`
  --> src/main.rs:19:11
   |
19 |     items.sort_by_key(key);
   |           ^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#16r

我不明白为什么会出现这个错误,所以我试着追踪这个错误。我首先实现了自己的sort_by_key()版本:

fn sort_by_key<T, K: Ord>(a: &mut [T], key: fn(&T) -> K) {
    a.sort_by(|x, y| key(x).cmp(&key(y)));
}

当试图调用此函数时,我得到的是“相反”错误:

error[E0308]: mismatched types
  --> src/main.rs:22:29
   |
22 |     sort_by_key(&mut items, key);
   |                             ^^^ expected concrete lifetime, found bound lifetime parameter
   |
   = note: expected type `fn(&std::string::String) -> _`
              found type `fn(&std::string::String) -> &std::string::String {main::key}`

我可以通过将密钥类型修改为&T而不是使用通用参数K,或使用&K而不是K作为返回类型来编译此代码关键功能:

fn sort_by_key_v2<T: Ord>(a: &mut [T], key: fn(&T) -> &T) {
    a.sort_by(|x, y| key(x).cmp(&key(y)));
}
fn sort_by_key_v3<T, K: Ord>(a: &mut [T], key: fn(&T) -> &K) {
    a.sort_by(|x, y| key(x).cmp(&key(y)));
}

我也尝试过添加生命周期注释,但这只会在不解决错误的情况下改变错误。

Here's the three versions of the sort_by_key() function on the Playground

为什么我会收到这些错误?有没有办法解决这些问题,同时保持密钥类型K完全通用?

3 个答案:

答案 0 :(得分:7)

  

为什么我会收到这些错误?有没有办法解决它们?

原因和修复是一回事:Rust目前的表达力不足以代表你想要的东西。所需的功能称为generic associated types (GATs);以前称为关联类型构造函数(ATC)或更高级别的类型(HKT)。

来自associated issue

  

要使sort_by_key调用合适,输入引用的生命周期[...]需要合并到B中以生成返回类型&'a str,但{{ 1}}是一个类型参数。

我不知道B的签名是否能够在实施后无缝移动到GAT。

目前,您必须使用“长”形式:

sort_by_key

答案 1 :(得分:2)

作为@Shepmaster explained,您不能拥有sort_by_key函数来处理key函数的返回类型的通用关联生命周期,但这里是一个关键函数的变体,总是返回一个引用:

fn sort_by_key_ref<T, F, K>(a: &mut [T], key: F) 
where
    F: Fn(&T) -> &K,
    K: ?Sized + Ord,
{
    a.sort_by(|x, y| key(x).cmp(key(y)));
}

您还可以记下关键功能的生命周期要求:

    for<'a> F: Fn(&'a T) -> &'a K,

请参阅example on playground

答案 2 :(得分:1)

基于Shepmaster的回答,最干净的解决方法(IMO)是:

library(tidyverse)
library(dplyr)
library(knitr)


isok.60 <- read.csv("~/Documents/StrengthTest/strengthTests.csv")%>%
  filter(!is.na(load), timepoint %in% c("pre", "post"),
         exercise == "isok.60") %>%
     mutate(timepoint = if_else(timepoint == "session1",
                             "pre", timepoint)) %>%
  group_by(subject, timepoint, group, sex) %>%
  summarise(load = mean(load, na.rm = TRUE)) %>%
  pivot_wider(names_from = timepoint, 
              values_from = load) %>%
  mutate(change = ((post - pre)/pre) * 100,
         pre.mc = pre - mean(pre))%>% 
  ungroup() %>%
  mutate(pre.mc = pre - mean(pre)) 

只需将fn ref_key<T, K: Ord + ?Sized>(mut f: impl FnMut(&T) -> &K) -> impl FnMut(&T, &T) -> Ordering { move |a, b| f(a).cmp(f(b)) } 转换为slice.sort_by_key(key)即可使用。