具有通用特征和slice :: sort_by的生命周期问题

时间:2018-10-30 21:00:48

标签: rust

作为一项学习练习,我一直在编写排序库,但遇到了障碍。我已经定义了特征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>

2 个答案:

答案 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: &Tb: &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)
});

另请参阅:

相关问题