我试图使用一个键函数对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)));
}

我还try 添加生存期注释,但这只会改变错误,而不会解决它.

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

为什么我会犯这些错误?有没有办法在保持密钥类型K完全通用的同时修复它们?

推荐答案

现在,你必须使用"长"形式:

v.sort_by(|x, y| key(x).cmp(&key(y)));

为什么我会犯这些错误?有没有办法修复它们?

原因和解决办法是一样的:Rust 目前还不足以表达你想要的东西.所需的功能称为generic associated types (GATs);以前被称为associated type constructors(ATC)或更高种类(HKT).

associated issue人中:

要使sort_by_key调用正常,输入引用的生存期[…]需要合并到B中才能生成返回类型&'a str,但B是一个类型参数.

我不知道sort_by_key的签名在实施时是否能够无缝地移动到GAT.


在控制所有类型签名的类似情况下,可以要求返回引用:

use std::cmp::Ordering;

struct User {
    name: String,
}

fn compare_keys<T, R>(a: T, b: T, key: impl Fn(&T) -> &R) -> Ordering
where
    for<'a> &'a R: Ord,
{
    let ak = key(&a);
    let bk = key(&b);
    ak.cmp(&bk)
}

fn main() {
    let alice = User {
        name: String::from("alice"),
    };
    let bob = User {
        name: String::from("bob"),
    };

    compare_keys(alice, bob, |u| &u.name);
}

这是不理想的,因为现在不能返回非引用,但在GATs实施之前,根本没有完整的解决方案.根据具体情况,您可以添加类似sort_bysort_by_key的并行方法.

Rust相关问答推荐

trait 中self 的显式生命周期似乎导致E0499无法在循环中多次borrow * emits 器作为可变的

如何在rust中有条件地分配变量?

基于对vec值的引用从该值中删除该值

为什么拥有的trait对象的相等运算符移动了正确的操作数?

什么样的 struct 可以避免使用RefCell?

MutexGuard中的过滤载体不需要克隆

将数组转换为HashMap的更简单方法

有没有可能让泛型Rust T总是堆分配的?

零拷贝按步骤引用一段字节

为什么在 Allocator API 中 allocate() 使用 `[u8]` 而 deallocate 使用 `u8` ?

try 实现线程安全的缓存

如何使用tracing-subscriberRust crate 构建多编写者、全局过滤订阅者

如何正确使用git2::Remote::push?

我可以用 Rust 编写一个不可变变量

分配给下划线模式时会发生什么?

在 Rust 中实现资源消耗的安全包装器

&str 的编译时拆分是否可能?

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

为什么-x试图解析为文字并在声明性宏中失败?

为什么 u64::trailing_zeros() 在无分支工作时生成分支程序集?