作为一项学习练习,我一直在编写排序库,但遇到了障碍。我已经定义了特征ExtractFrom
,以便从切片中的项目中提取可排序的键(相当于sort_by_key
的操作)。我希望能够提取借用数据的密钥,但是我实现该密钥的尝试失败了。
这是一个简化的示例,演示了我的尝试。 LargeData
是切片中包含的内容,并且我定义了LargeDataKey
,其中包含对我要作为排序依据的数据子集的引用。在extract_from
实现与sort_by
期望之间存在生命周期问题,但是我不知道如何解决。任何有关如何最好地做到这一点的解释或建议,将不胜感激。
trait ExtractFrom<'a, T> {
type Extracted;
fn extract_from(&'a T) -> Self::Extracted;
}
fn sort_by_extractor<'a, T, E>(vec: Vec<T>)
where
E: ExtractFrom<'a, T>,
E::Extracted: Ord,
{
vec.sort_by(|a, b| {
let ak = &E::extract_from(a);
let bk = &E::extract_from(b);
ak.cmp(bk)
})
}
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
struct LargeData(String, String, String);
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
struct LargeDataKey<'a>(&'a str, &'a str);
impl<'a> ExtractFrom<'a, LargeData> for LargeDataKey<'a> {
type Extracted = LargeDataKey<'a>;
fn extract_from(input: &'a LargeData) -> LargeDataKey<'a> {
LargeDataKey(&input.2, &input.0)
}
}
fn main() {
let v = vec![
LargeData("foo".to_string(), "bar".to_string(), "baz".to_string()),
LargeData("one".to_string(), "two".to_string(), "three".to_string()),
LargeData("four".to_string(), "five".to_string(), "six".to_string()),
];
sort_by_extractor::<LargeData, LargeDataKey>(v);
println!("hello");
}
此代码也可以在Rust playground上找到。
此操作失败,并显示以下信息:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/main.rs:12:19
|
12 | let ak = &E::extract_from(a);
| ^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 11:17...
--> src/main.rs:11:17
|
11 | vec.sort_by(|a, b| {
| _________________^
12 | | let ak = &E::extract_from(a);
13 | | let bk = &E::extract_from(b);
14 | | ak.cmp(bk)
15 | | })
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:12:35
|
12 | let ak = &E::extract_from(a);
| ^
note: but, the lifetime must be valid for the lifetime 'a as defined on the function body at 6:22...
--> src/main.rs:6:22
|
6 | fn sort_by_extractor<'a, T, E>(vec: Vec<T>)
| ^^
= note: ...so that the types are compatible:
expected ExtractFrom<'_, T>
found ExtractFrom<'a, T>
答案 0 :(得分:1)
编译器错误明确指出此处有两个生存期:
vec.sort_by(|a: &T, b: &T| {
let ak = &E::extract_from(a);
let bk = &E::extract_from(b);
ak.cmp(bk)
})
a: &T
和b: &T
闭包参数相关的匿名生存期'a
生存期参数(fn extract_from(&'a T)
)相关联的生存期在维护您的设计时,我找不到消除这种终身不匹配的方法。
如果您的目标是从切片中的项目中提取可排序的键,则此方法的工作原理是为Ord
实现LargeData
:
use std::cmp::Ordering;
#[derive(Debug, PartialOrd, PartialEq, Eq)]
struct LargeData(String, String, String);
// really needed?
// see impl in LargeData::cmp() below
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
struct LargeDataKey<'a>(&'a str, &'a str);
impl Ord for LargeData {
fn cmp(&self, other: &LargeData) -> Ordering {
//let op1 = LargeDataKey(&self.2, &self.0);
//let op2 = LargeDataKey(&other.2, &other.0);
//op1.cmp(&op2)
(&self.2, &self.0).cmp(&(&other.2, &other.0))
}
}
fn sort_by_extractor<E, T>(vec: &mut Vec<T>, extractor: E)
where
E: FnMut(&T, &T) -> Ordering,
{
vec.sort_by(extractor);
}
fn main() {
let mut v = vec![
LargeData("foo".to_string(), "bar".to_string(), "baz".to_string()),
LargeData("one".to_string(), "two".to_string(), "three".to_string()),
LargeData("four".to_string(), "five".to_string(), "six".to_string()),
];
sort_by_extractor(&mut v, |a, b| a.cmp(b));
println!("{:?}", v);
}
答案 1 :(得分:0)
您的代码更有可能写为
#[derive(Debug)]
struct LargeData(String, String, String);
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
struct LargeDataKey<'a>(&'a str, &'a str);
impl<'a> From<&'a LargeData> for LargeDataKey<'a> {
fn from(input: &'a LargeData) -> LargeDataKey<'a> {
LargeDataKey(&input.2, &input.0)
}
}
fn main() {
let mut v = vec![
LargeData("foo".to_string(), "bar".to_string(), "baz".to_string()),
LargeData("one".to_string(), "two".to_string(), "three".to_string()),
LargeData("four".to_string(), "five".to_string(), "six".to_string()),
];
v.sort_by_key(|x| LargeDataKey::from(x));
println!("hello");
}
与rodrigo suggests一样,您的代码无法在稳定的Rust 1.30中实现。这是为什么 sort_by_key
的局限性:目前尚无法设计一个涵盖用例的特征。
问题在于Rust目前没有generic associated types的概念。为了能够定义一个关联类型,而该关联类型的构造函数可能会占用生命周期的后期,就需要这样做。
您可以直接使用sort_by
,只要返回的类型不能逃脱闭包即可:
v.sort_by(|a, b| {
let a = LargeDataKey::from(a);
let b = LargeDataKey::from(b);
a.cmp(&b)
});
另请参阅: