Встраивать заимствованное значение в перечисление?

У меня проблемы со следующим кодом:

trait HelloPhrase {
    fn hello(&self, to: &'static str);
}

pub enum GetHelloResult<H: HelloPhrase> {
    Matched(H),
    NoMatch,
}

struct English;

impl English {
    pub fn new() -> English {
        English
    }
}

impl HelloPhrase for English {
    fn hello(&self, to: &'static str) {
        println!("Hello {}.", to)
    }
}

struct Phrases<H: HelloPhrase> {
    hello_phrases: std::collections::HashMap<&'static str, H>,
}

impl<H: HelloPhrase> Phrases<H> {
    pub fn new() -> Phrases<H> {
        Phrases { hello_phrases: std::collections::HashMap::new() }
    }

    pub fn add_hello_phrase(&mut self, lang: &'static str, hello_phrase: H) {
        self.hello_phrases.insert(lang, hello_phrase);
    }

    pub fn get_hello(&self, lang: &'static str) -> GetHelloResult<H> {
        match self.hello_phrases.get(lang) {
            Some(hello_phrase) => return GetHelloResult::Matched(hello_phrase),
            _ => return GetHelloResult::NoMatch,
        };
    }
}

fn main() {
    let mut hs = Phrases::new();
    hs.add_hello_phrase("english", English::new());

    match hs.get_hello("english") {
        GetHelloResult::Matched(hello_phrase) => hello_phrase.hello("Tom"),
        _ => println!("HelloPhrase not found"),
    }
}

(воспроизвести ссылку)

HelloPhrase - это особенность языка, который нужно реализовать, английский, русский и т. Д. Phrases - это управляющая структура, которая может иметь множество карт преобразования языка в фразу. Это надуманный пример, но вы можете рассматривать его как диспетчер событий (то есть получать обработчики событий для ввода X) или как обработчик HTTP и маршрутизатор.

С учетом сказанного, мне трудно понять, как заимствовать право собственности на HelloPhrase, чтобы вернуть его вызывающей стороне. При его запуске возвращается следующая ошибка:

<anon>:40:66: 40:78 error: mismatched types:
 expected `H`,
    found `&H`
(expected type parameter,
    found &-ptr) [E0308]
<anon>:40             Some(hello_phrase) => return GetHelloResult::Matched(hello_phrase),
                                                                           ^~~~~~~~~~~~

Я пробовал добавить:

pub fn get_hello(&self, lang: &'static str) -> GetHelloResult<&H> {

а также

pub enum GetHelloResult<H: HelloPhrase> {
    Matched(&H),
    NoMatch,
}

(воспроизвести ссылку)

что приводит к следующей ошибке:

<anon>:7:13: 7:15 error: missing lifetime specifier [E0106]
<anon>:7     Matched(&H),

У меня возникли проблемы с добавлением времени жизни в перечисление - теоретически я хочу, чтобы время жизни возвращаемого значения было временем жизни структуры Phrases, но синтаксис времени жизни пока меня довольно сбивает с толку. Подводя итог, можно сформулировать два вопроса:

  1. Как мне добавить время жизни к GetHelloResult, чтобы устранить эту ошибку?
  2. Основываясь на правилах владения в Rust, могу ли я сделать анти-паттерн в Rust? Что может быть лучше для чего-то подобного?

Основываясь на документации, я знаю, как использовать время жизни в структуре, но я не знаю, как добавить время жизни к перечислению (с точки зрения синтаксиса). Я упомянул время жизни структуры только потому, что предполагаю, что это недостающая часть, но, честно говоря, я не знаю. Кроме того, если я добавляю время жизни к структуре и impl и пытаюсь добавить его на карту hello_phrases, я получаю сообщение об ошибке

the parameter type `H` may not live long enough [E0309]

person FizzBazer    schedule 11.04.2016    source источник
comment
Поэтому я стараюсь не предполагать слишком многого и просто следую тому, к чему меня привели сообщения об ошибках. Который до сих пор ничего не упоминал о времени жизни полей структуры.   -  person FizzBazer    schedule 11.04.2016


Ответы (2)


Путаница здесь - досадный побочный эффект пожизненной элизии. Это помогает в 99% случаев, но не всегда обнаруживается.

Вам нужно аннотировать GetHelloResult временем жизни:

pub enum GetHelloResult<'a, H: 'a + HelloPhrase> {
    Matched(&'a H),
    NoMatch,
}

pub fn get_hello(&self, lang: &'static str) -> GetHelloResult<H> {
    match self.hello_phrases.get(lang) {
        Some(hello_phrase) => return GetHelloResult::Matched(hello_phrase),
        _ => return GetHelloResult::NoMatch,
    };
}

Это связывает время жизни GetHelloResult со временем жизни структуры Phrases, так что если структура Phrases изменена (или разрушена!), Возвращенная ссылка будет недействительной. В данном случае предполагается, что время жизни будет таким же, как у self, что не очевидно при чтении, но это правда! В менее очевидных ситуациях вы можете явно добавить аннотацию с помощью GetHelloResult<'a, H>.

Воспроизвести ссылку.

person Clark Gaebel    schedule 11.04.2016

Код отлично работает, когда вы возвращаете ссылку (fn get_hello(&self, lang: &'static str) -> GetHelloResult<&H>), если вы реализуете трейт для ссылок на типы, которые также реализуют трейт:

impl<'a, H> HelloPhrase for &'a H 
    where H: HelloPhrase
{
    fn hello(&self, to: &'static str) {
        (**self).hello(to)
    }
}
person Shepmaster    schedule 11.04.2016