将自由函数迁移到特征时的生命周期不匹配

时间:2018-04-13 16:05:18

标签: rust traits

我实现了这个小函数,它返回对枚举值的引用。这很好用:

pub enum Entry {
    COMMENT,
    NUM(Option<String>, f64),
    STR(Option<String>, String),
    VNUM(Option<String>, Vec<f64>),
    VSTR(Option<String>, Vec<String>),
}

pub fn get_value<'b>(k: &str, entity: &'b [Entry]) -> Option<&'b str> {
    entity
        .iter()
        .filter_map(|x| match x {
            &Entry::STR(ref key, ref v) => if key.as_ref().unwrap().as_str() == k {
                Some(v.as_str())
            } else {
                None
            },
            _ => None,
        })
        .next()
}
fn main() {
    let v = vec![
        Entry::STR(Some("foo".to_string()), "bar".to_string()),
        Entry::NUM(Some("baz".to_string()), 1234f64),
    ];
    let x: Option<&str> = get_value("foo", &v);
}

为了灌输一些多态性,我想把它转移到一个特性,以便我可以为不同的类型调用它

pub trait GetValue<T> {
    fn get_value<'a>(k: &str, entity: &'a [Entry]) -> Option<&'a str>;
}

impl<'a> GetValue<&'a str> for &'a [Entry] {
    fn get_value(k: &str, entity: &'a [Entry]) -> Option<&'a str> {
        entity
            .iter()
            .filter_map(|x| match x {
                &Entry::STR(ref key, ref v) => if key.as_ref().unwrap().as_str() == k {
                    Some(v.as_str())
                } else {
                    None
                },
                _ => None,
            })
            .next()
    }
}

但是尽管没有故意更改生命周期规范,但仍会出现以下错误。我错过了什么?

error[E0308]: method not compatible with trait
  --> src/main.rs:14:5
   |
14 |     fn get_value(k: &str, entity: &'a [Entry]) -> Option<&'a str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected type `fn(&str, &'a [Entry]) -> std::option::Option<&'a str>`
              found type `fn(&str, &'a [Entry]) -> std::option::Option<&'a str>`
note: the lifetime 'a as defined on the method body at 14:5...
  --> src/main.rs:14:5
   |
14 |     fn get_value(k: &str, entity: &'a [Entry]) -> Option<&'a str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 13:1
  --> src/main.rs:13:1
   |
13 | impl<'a> GetValue<&'a str> for &'a [Entry] {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: method not compatible with trait
  --> src/main.rs:14:5
   |
14 |     fn get_value(k: &str, entity: &'a [Entry]) -> Option<&'a str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected type `fn(&str, &'a [Entry]) -> std::option::Option<&'a str>`
              found type `fn(&str, &'a [Entry]) -> std::option::Option<&'a str>`
note: the lifetime 'a as defined on the impl at 13:1...
  --> src/main.rs:13:1
   |
13 | impl<'a> GetValue<&'a str> for &'a [Entry] {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the method body at 14:5
  --> src/main.rs:14:5
   |
14 |     fn get_value(k: &str, entity: &'a [Entry]) -> Option<&'a str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

2 个答案:

答案 0 :(得分:1)

通过在&'a [Entry]方法的签名中分别用&'a strSelf替换Tget_value,可以消除特征中的显式生命周期定义。因此,GetValue的定义独立于&'a [Entry]&'a str类型,这些类型特定于&'a [Entry]的具体实现。作为副作用,您的终身问题将会消失。

pub trait GetValue<T> {
    fn get_value(self, k: &str) -> Option<T>;
}

impl<'a> GetValue<&'a str> for &'a [Entry] {
    fn get_value(self, k: &str) -> Option<&'a str> {
        self.iter()
            .filter_map(|x| match x {
                &Entry::STR(ref key, ref v) => 
                    if key.as_ref().unwrap().as_str() == k {
                        Some(v.as_str())
                    } else {
                        None
                    },
                _ => None,
            })
            .next()
    }
}

这样,get_value消费self。在&'a [Entry]的实施中,方法get_value会使用&'a [Entry]类型的内容。不可变引用是Copy,因此在此实现中get_value实际上只使用副本(&'a [Entry]类型的内容在调用get_value之后仍然可用。

GetValue也可以针对非Copy的内容实施。在这种类型的实例上调用get_value将使用此实例。如果您希望get_value在这方面更具限制性,可以让&'a self作为参数而不是self。这需要在您的特征上明确生存'a

pub trait GetValue<'a, T> {
    fn get_value(&'a self, k: &str) -> Option<T>;
}

impl<'a> GetValue<'a, &'a str> for [Entry] {
    fn get_value(&'a self, k: &str) -> Option<&'a str> { ... }
}

答案 1 :(得分:0)

通过考虑trait lifetime subtyping

来解决

使用相关生命周期定义特征:

pub trait GetValue<'c,T> {
    fn get_value<'a : 'c>(&'c self, k : &str, entity : &'a [Entry]) -> Option<T>;
}

然后执行告诉Rust返回类型依赖于self而不是entity

impl<'c> GetValue<'c,&'c str> for &'c [Entry] {
    fn get_value<'a : 'c>(&'c self, k : &str, entity : &'a [Entry]) -> Option<&'c str> {
        entity
        .iter()
        .filter_map(|x| match x {
            &Entry::STR(ref key,ref v) => if key.as_ref().unwrap().as_str() == k { Some(v.as_str()) } else { None },
            _ => None
        })
            .next()
    }
}