2016-03-31 4 views
0

У меня есть метод ржавчины, который возвращает Result<_, MyError>. Этот метод запускается в структуре State, а MyError имеет спецификатор времени жизни 'a, так как он должен содержать &str с.Как вернуть ошибку, когда она содержит ссылку?

Я пытаюсь написать черту так:

trait MyTrait { 
    type Error; 

    fn work(&self) -> Result<(), Self::Error>; 
} 

impl<'a> MyTrait for MyImpl<'a> { 
    type Error = MyError<'a>; 

    fn work(&self) -> Result<(), MyError<'a>> { 
     let state = State::new(); 

     state.work() // returns Result<(), MyError> but state doesn't live long enough 
    } 
} 

Как я могу получить эту ошибку? Должен ли я изменить MyError, чтобы сохранить String вместо &'a str? Должен ли я держать state внутри MyImpl? Действительно ли это trait?

Я хочу создать State за каждый пробег do().

Вот MCVE:

enum MyError<'a> { 
    Some(&'a str), 
} 

trait MyTrait { 
    type Error; 

    fn work(&self) -> Result<(), Self::Error>; 
} 

struct MyImpl<'a> { 
    pub some_string: &'a str, 
} 

impl<'a> MyTrait for MyImpl<'a> { 
    type Error = MyError<'a>; 

    fn work(&self) -> Result<(), MyError<'a>> { 
     let state = State::new(); 

     state.work() // returns Result<(), MyError> but state doesn't live long enough 
    } 
} 

struct State; 

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

    pub fn work(&self) -> Result<(), MyError> { 
     Err(MyError::Some("hi")) 
    } 
} 

fn main() {} 

(Playground)

+0

Вы также не указали определение 'MyError' и как именно' State :: work() 'создает' MyError'. Если он действительно содержит строковые срезы, привязанные к времени жизни 'state', было бы невозможно вернуть' MyError', потому что 'state' уничтожается прямо в' MyImpl :: work() '. –

+0

@VladimirMatveev Это, очевидно, проблема, но как я могу привязать их к 'MyError' вместо этого? – dragostis

ответ

2

Проблема заключается в том, что в соответствии с подписью State::work() параметр времени жизни MyError становится привязана к &self справки:

// without lifetime elision 
pub fn work<'a>(&'a self) -> Result<(), MyError<'a>> 

И после этого это значение возвращается в MyImpl::work():

fn work(&self) -> Result<(), MyError<'a>> { 
    let state = State::new(); 

    state.work() 
} 

Проблема заключается в том, параметр времени жизни 'a в impl<'a> MyTrait for MyImpl<'a> обозначает срок службы, который строго больше, чем у MyError возвращенный State::work(). Почему это так? Ну, давайте еще раз посмотрим на MyImpl::work():

fn work(&self) -> Result<(), MyError<'a>> { 
    let state = State::new(); 
    state.work() 
} 

Помните, что State::work(&self) возвращает MyError с жизни, привязанной к тому, что из &self, то есть, в данном случае это будет срок службы state. Последний, являясь локальной переменной, уничтожается сразу после возврата work().

Однако 'a в impl<'a> MyTrait for MyImpl<'a> обозначает время жизни строки среза, хранящейся в MyImpl (то есть, в self). Естественно, потому что MyImpl::work() может быть вызван вообще, это означает, что значение, которое он вызывается, находится в правильном состоянии и содержит оживленный срез. Поэтому его время жизни больше, чем все, что может быть создано внутри MyImpl::work(). Поэтому необоснованно возвращать все, что не выводится из этого строкового фрагмента внутри MyImpl; например, это справедливо:

impl<'a> MyTrait for MyImpl<'a> { 
    type Error = MyError<'a>; 

    fn work(&self) -> Result<(), MyError<'a>> { 
     Err(MyError::Some(self.some_string)) 
    } 
} 

В настоящее время жизни MyError значение в точности совпадает с self.some_string, а заимствуют шашка становится счастливым.

Теперь, какие у вас варианты?Во-первых, самый простой подход будет хранить находящееся в собственности String внутри MyError:

enum MyError { 
    Some(String) 
} 

impl<'a> MyTrait for MyImpl<'a> { 
    type Error = MyError; 

    fn work(&self) -> Result<(), MyError> { 
     let state = State::new(); 
     state.work() 
    } 
} 

struct State; 

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

    pub fn work(&self) -> Result<(), MyError> { 
     Err(MyError::Some("hi".into())) 
    } 
} 

Это, я считаю, является наиболее идиоматических и наиболее гибкий подход. На самом деле очень редко встречаются несамостоятельные значения ошибок; Кажется, я никогда не видел его раньше. Другой альтернативой было бы использовать &'static str:

enum MyError { 
    Some(&'static str) 
} 

struct State; 

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

    pub fn work(&self) -> Result<(), MyError> { 
     Err(MyError::Some("hi")) 
    } 
} 

Такой подход не позволит вам создавать сообщения об ошибках динамически (вы можете использовать только строковые литералы для ваших сообщений об ошибках), но это является более эффективным, поскольку он не требует распределения для несчастливого пути в вашей программе, и этого может быть достаточно для ваших случаев использования.

Смежные вопросы