简体   繁体   English

实现特征时如何明确指定生存期?

[英]How can I explicitly specify a lifetime when implementing a trait?

Given the implementation below, where essentially I have some collection of items that can be looked up via either a i32 id field or a string field. 给定以下实现,本质上我可以通过i32 id字段或字符串字段来查找一些项目集合。 To be able to use either interchangeably, a trait "IntoKey" is used, and a match dispatches to the appropriate lookup map; 为了能够互换使用,使用了特征“ IntoKey”,并且将一个match分配到适当的查找映射。 this all works fine for my definition of get within the MapCollection impl: 对于我在MapCollection impl中get定义,这一切都很好:

use std::collections::HashMap;
use std::ops::Index;

enum Key<'a> {
    I32Key(&'a i32),
    StringKey(&'a String),
}

trait IntoKey<'a> {
    fn into_key(&'a self) -> Key<'a>;
}

impl<'a> IntoKey<'a> for i32 {
    fn into_key(&'a self) -> Key<'a> { Key::I32Key(self) }
}

impl<'a> IntoKey<'a> for String {
    fn into_key(&'a self) -> Key<'a> { Key::StringKey(self) }
}

#[derive(Debug)]
struct Bar {
    i: i32,
    n: String,
}

struct MapCollection
{
    items: Vec<Bar>,
    id_map: HashMap<i32, usize>,
    name_map: HashMap<String, usize>,
}

impl MapCollection {
    fn new(items: Vec<Bar>) -> MapCollection {
        let mut is = HashMap::new();
        let mut ns = HashMap::new();
        for (idx, item) in items.iter().enumerate() {
            is.insert(item.i, idx);
            ns.insert(item.n.clone(), idx);
        }
        MapCollection {
            items: items,
            id_map: is,
            name_map: ns,
        }
    }

    fn get<'a, K>(&self, key: &'a K) -> Option<&Bar>
        where K: IntoKey<'a> //'
    {
        match key.into_key() {
            Key::I32Key(i)    => self.id_map.get(i).and_then(|idx|     self.items.get(*idx)),
            Key::StringKey(s) => self.name_map.get(s).and_then(|idx|     self.items.get(*idx)),
        }
    }
}

fn main() {
    let bars = vec![Bar { i:1, n:"foo".to_string() }, Bar { i:2, n:"far".to_string() }];
    let map = MapCollection::new(bars);
    if let Some(bar) = map.get(&1) {
        println!("{:?}", bar);
    }
    if map.get(&3).is_none() {
        println!("no item numbered 3");
    }
    if let Some(bar) = map.get(&"far".to_string()) {
        println!("{:?}", bar);
    }
    if map.get(&"baz".to_string()).is_none() {
        println!("no item named baz");
    }
}

However, if I then want to implement std::ops::Index for this struct, if I attempt to do the below: 但是,如果我随后想为此结构体实现std::ops::Index ,如果我尝试执行以下操作:

impl<'a, K> Index<K> for MapCollection
where K: IntoKey<'a> {
    type Output = Bar;

    fn index<'b>(&'b self, k: &K) -> &'b Bar {
        self.get(k).expect("no element")
    }
}

I hit a compiler error: 我遇到了一个编译器错误:

src/main.rs:70:18: 70:19 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
src/main.rs:70         self.get(k).expect("no element")
                            ^
src/main.rs:69:5: 71:6 help: consider using an explicit lifetime parameter as shown: fn index<'b>(&'b self, k: &'a K) -> &'b Bar
src/main.rs:69     fn index<'b>(&'b self, k: &K) -> &'b Bar {
src/main.rs:70         self.get(k).expect("no element")
src/main.rs:71     }

I can find no way to specify a distinct lifetime here; 我找不到在这里指定不同生命周期的方法。 following the compiler's recommendation is not permitted as it changes the function signature and no longer matches the trait, and anything else I try fails to satisfy the lifetime specification. 不允许遵循编译器的建议,因为它会更改函数签名并且不再与特征匹配,并且我尝试的任何其他操作都无法满足生命周期规范。

I understand that I can implement the trait for each case (i32, String) separately instead of trying to implement it once for IntoKey, but I am more generally trying to understand lifetimes and appropriate usage. 我知道我可以为每种情况(i32,String)分别实现特征,而不是尝试对IntoKey一次实现特征,但是我更普遍地试图理解生存期和适当用法。 Essentially: 实质上:

  • Is there actually an issue the compiler is preventing? 编译器实际上是否在阻止一个问题? Is there something unsound about this approach? 这种方法有什么不完善的地方吗?
  • Am I specifying my lifetimes incorrectly? 我的寿命是否指定错误? To me, the lifetime 'a in Key/IntoKey is dictating that the reference need only live long enough to do the lookup; 对我而言,Key / IntoKey中的生存期'a指示该引用只需要生存足够长的时间即可进行查找; the lifetime 'b associated with the index fn is stating that the reference resulting from the lookup will live as long as the containing MapCollection . index fn相关联的有效期'b表示,只要包含MapCollection ,查找所产生的引用将一直MapCollection
  • Or am I simply not utilizing the correct syntax to specify the needed information? 还是我只是没有利用正确的语法来指定所需的信息?

(using rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000) ) (使用rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000)

Do you intend on implementing IntoKey on struct's that are going to store references of lifetime 'a ? 您是否打算在将存储生命周期'a引用的struct上实现IntoKey If not, you can change your trait and its implementations to: 如果没有,您可以将特征及其实现更改为:

trait IntoKey {
    fn into_key<'a>(&'a self) -> Key<'a>;
}

This is the generally recommended definition style, if you can use it. 如果可以使用,这是通常推荐的定义样式。 If you can't... 如果不能

Let's look at this smaller reproduction: 让我们看一下这个较小的复制品:

use std::collections::HashMap;
use std::ops::Index;

struct Key<'a>(&'a u8);

trait IntoKey<'a> { //'
    fn into_key(&'a self) -> Key<'a>;
}

struct MapCollection;

impl MapCollection {
    fn get<'a, K>(&self, key: &'a K) -> &u8
        where K: IntoKey<'a> //'
    {
        unimplemented!()
    }
}

impl<'a, K> Index<K> for MapCollection //'
    where K: IntoKey<'a> //'
{
    type Output = u8;

    fn index<'b>(&'b self, k: &K) -> &'b u8 { //'
        self.get(k)
    }
}

fn main() {
}

The problem lies in get : 问题出在get

fn get<'a, K>(&self, key: &'a K) -> &u8
    where K: IntoKey<'a>

Here, we are taking a reference to K that must live as long as the Key we get out of it. 在这里,我们参考的是K只要它脱离了Key ,它就必须存在。 However, the Index trait doesn't guarantee that: 但是,索引特征不能保证:

fn index<'b>(&'b self, k: &K) -> &'b u8

You can fix this by simply giving a fresh lifetime to key : 您可以通过简单地给key赋予新生命来解决此问题:

fn get<'a, 'b, K>(&self, key: &'b K) -> &u8
    where K: IntoKey<'a>

Or more succinctly: 或更简洁地说:

fn get<'a, K>(&self, key: &K) -> &u8
    where K: IntoKey<'a>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM