Учитывая приведенную ниже реализацию, где, по сути, у меня есть некоторая коллекция элементов, которые можно найти либо через поле идентификатора i32, либо через строковое поле. Чтобы иметь возможность использовать их взаимозаменяемо, используется типаж «IntoKey», и match
отправляется на соответствующую карту поиска; все это отлично работает для моего определения get
в MapCollection
impl:
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, String) отдельно, вместо того, чтобы пытаться реализовать его один раз для IntoKey, но в целом я пытаюсь понять время жизни и правильное использование. По сути:
- Есть ли проблема, которую предотвращает компилятор? Есть ли что-то необоснованное в таком подходе?
- Я неправильно указываю время своей жизни? Для меня время жизни
'a
в Key / IntoKey диктует, что ссылка должна существовать достаточно долго, чтобы выполнить поиск; время жизни'b
, связанное сindex
fn, указывает, что ссылка, полученная в результате поиска, будет существовать до тех пор, пока содержащаяMapCollection
. - Или я просто не использую правильный синтаксис для указания необходимой информации?
(с использованием rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000)
)