Как я могу явно указать время жизни при реализации трейта?

Учитывая приведенную ниже реализацию, где, по сути, у меня есть некоторая коллекция элементов, которые можно найти либо через поле идентификатора 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))


person nogoma    schedule 18.02.2015    source источник
comment
Добро пожаловать в Stack Overflow! Чтобы получить качественные ответы и, возможно, помочь вам лучше понять вашу проблему, уделите время ответам на будущие вопросы, чтобы сделать MCVE . Вот один, над которым я сейчас работаю в качестве примера.   -  person Shepmaster    schedule 18.02.2015
comment
@Shepmaster Спасибо за подсказку; Я попытался сделать свой фрагмент как можно более минимальным, но, поскольку я не был уверен, в чем была моя проблема, мне было трудно обрезать его до самого необходимого. Кстати, я видел много ваших ответов на вопросы о ржавчине по SO, и они всегда были информативными. Спасибо!   -  person nogoma    schedule 18.02.2015
comment
Не беспокойтесь, я знаю, что создание минимальных примеров может быть трудным! Я просто напоминаю людям, когда это возможно. ^ _ ^   -  person Shepmaster    schedule 18.02.2015


Ответы (1)


Планируете ли вы реализовать IntoKey в структурах, которые будут хранить ссылки времени жизни 'a? Если нет, вы можете изменить свой трейт и его реализации на:

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>
person Shepmaster    schedule 18.02.2015
comment
Оба ваших объяснения действительно помогли мне понять время жизни; для своих нужд я без надобности привязывал это к черте IntoKey. Но ваше альтернативное решение для случаев, когда IntoKey может потребоваться ограничение срока службы, было очень поучительным; Я не понял ограничения, которое налагал на аргумент key для get. - person nogoma; 18.02.2015