rust - 如何在实现特征时明确指定生命周期?

标签 rust lifetime

鉴于下面的实现,基本上我有一些可以通过 i32 id 字段或字符串字段查找的项目集合。为了能够互换使用,使用了特征“IntoKey”和 match分派(dispatch)到适当的查找 map ;这一切都适用于我对 get 的定义在 MapCollection 内实现:

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");
    }
}

但是,如果我想执行 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")
    }
}

我遇到了一个编译器错误:

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     }

我找不到在这里指定不同生命周期的方法;遵循编译器的建议是不允许的,因为它更改了函数签名并且不再匹配特征,而且我尝试的任何其他操作都无法满足生命周期规范。

我知道我可以分别为每个案例(i32、字符串)实现特征,而不是尝试为 IntoKey 实现一次,但我更普遍地试图了解生命周期和适当的用法。本质上:

  • 编译器是否真的阻止了一个问题?这种方法有什么不妥之处吗?
  • 我是否错误地指定了我的生命周期?对我来说,一生'a在 Key/IntoKey 中指示引用只需要足够长的时间来进行查找;一生'bindex 相关联fn 表示查找产生的引用将与包含 MapCollection 的引用一样长。 .
  • 或者我只是没有使用正确的语法来指定所需的信息?

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

最佳答案

您是否打算在将要存储生命周期 'a 引用的结构上实现 IntoKey?如果没有,您可以将特征及其实现更改为:

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

这是一般推荐的定义样式,如果你能用的话。如果你不能...

让我们看看这个较小的复制品:

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() {
}

问题出在get:

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

在这里,我们引用了 K,它必须只要我们从中得到的 Key 就存在。但是,Index 特征并不能保证:

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

您可以通过简单地为 key 赋予新的生命周期来解决这个问题:

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

或者更简洁:

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

关于rust - 如何在实现特征时明确指定生命周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28574458/

相关文章:

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 与(表面上)似乎完全安全的短暂生命周期值相混淆

sql - 使用 derive Insertable for float

syntax - 如何指定关联类型的生命周期?

rust - 由于生命周期/借用错误,文本文件解析函数无法编译

javascript - 为什么尝试访问 React 组件的类名会导致 ReferenceError ?

methods - 结构方法的Rust生命周期推断

rust - 多个可执行文件使用私有(private)模块的正确位置

rust - Rust 中的 HMAC-SHA1