2016-03-20 2 views
3

Будучи новичком Rust, я, вероятно, несколько наивно начал с этим:Почему моя черта нуждается в параметре lifetime?

... 

pub trait Decode<T> { 
    fn decode_from<R: io::Read + ?Sized>(&mut self, stream: &mut R) -> T; 
} 

pub struct MQTTFrame<'a> { 
    pub payload: &'a Vec<u8>, 
} 

pub struct MQTTFrameDecoder<'a> { 
    pub payload: &'a mut Vec<u8>, 
} 

impl<'a> Decode<MQTTFrame<'a>> for MQTTFrameDecoder<'a> { 
    fn decode_from<R: io::Read + ?Sized>(&mut self, stream: &mut R) -> MQTTFrame<'a> { 
     stream.read(&mut self.payload); 
     MQTTFrame{ payload: self.payload } 
    } 
} 

Который, при попытке компиляции, был встречен с:

src/testbed/mod.rs:31:24: 31:36 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements [E0495] 
src/testbed/mod.rs:31   MQTTFrame{ payload: self.payload } 
                ^~~~~~~~~~~~ 
src/testbed/mod.rs:29:5: 32:6 help: consider using an explicit lifetime parameter as shown: fn decode_from<R: io::Read + ?Sized>(&'a mut self, stream: &mut R) 
-> MQTTFrame<'a> 
src/testbed/mod.rs:29  fn decode_from<R: io::Read + ?Sized>(&mut self, stream: &mut R) -> MQTTFrame<'a> { 
src/testbed/mod.rs:30   stream.read(&mut self.payload); 
src/testbed/mod.rs:31   MQTTFrame{ payload: self.payload } 
src/testbed/mod.rs:32  } 

Где-то на StackOverflow - извините, я забыл, где - кто-то в подобном случае предложено добавить параметр пожизненную как так (опуская без изменений кода):

pub trait Decode<'a, T> { 
    fn decode_from<R: io::Read + ?Sized>(&'a mut self, stream: &mut R) -> T; 
} 

impl<'a> Decode<'a, MQTTFrame<'a>> for MQTTFrameDecoder<'a> { 
    fn decode_from<R: io::Read + ?Sized>(&'a mut self, stream: &mut R) -> MQTTFrame<'a> { 
     stream.read(&mut self.payload); 
     MQTTFrame{ payload: self.payload } 
    } 
} 

и вот, и вот! Он компилируется. Теперь, если бы я мог понять только , почему компилируется. Может ли кто-нибудь объяснить

  1. Почему исходный код не компилировался?
  2. Почему код изменен?

ответ

2

Вот уменьшена TestCase, которая не компилировать():

pub trait Decode<T> { 
    fn decode_from<'b>(&'b mut self) -> T; 
} 

pub struct MQTTFrame<'a> { 
    pub payload: &'a Vec<u8>, 
} 

pub struct MQTTFrameDecoder<'a> { 
    pub payload: &'a mut Vec<u8>, 
} 

impl<'a> Decode<MQTTFrame<'a>> for MQTTFrameDecoder<'a> { 
    fn decode_from<'b>(&'b mut self) -> MQTTFrame<'a> { 
     MQTTFrame{ payload: self.payload } 
    } 
} 

Обратите внимание, что у меня elided the lifetimes для функции decode_from и удалить избыточный параметр потока.

Понятно, что функция принимает задание со сколь угодно коротким временем жизни 'b, а затем продлевает его на время жизни 'a. Это проблема с изменяемой ссылкой, а затем вы можете взять что-то mutably и непреложен в то же время:

fn main() { 
    let mut v = vec![]; 
    /* lifetime 'a */ { 
     let mut decoder = MQTTFrameDecoder{ payload: &mut v }; 
     let frame: MQTTFrame; 
     /* lifetime 'b */ { 
      frame = decoder.decode_from(); // borrows decoder just for lifetime 'b 
     } 
     // v is mutably borrowed (by decoder) and immutably borrowed (by frame) at the same time! oops! 
     decoder.payload.push(1); 
     println!("{:?}", frame.payload); 
    } 
} 

По этой причине заема проверка отказывается позволить функцию компиляции.

Если вы вынуждены ссылаться на decoder, чтобы иметь срок службы 'a, тем не менее, проблема больше не возникает. Компилятор не может использовать ссылку с более коротким сроком службы, он должен долгое время заимствовать decoder, поэтому компилятор должен дать нам ошибку, когда мы попытаемся заимствовать ее снова.

Для того, чтобы достичь этого, мы хотели бы написать

fn decode_from(&'a mut self) -> MQTTFrame<'a> { 
    MQTTFrame{ payload: self.payload } 
} 

Но теперь мы получаем ошибку:

<anon>:14:5: 16:6 error: method `decode_from` has an incompatible type for trait: 
expected bound lifetime parameter 'b, 
    found concrete lifetime [E0053] 

Чтобы исправить это, мы должны иметь наш признак знать, что вы может только decode_from определенные времена жизни, а не произвольные.Меняет декодирование в

pub trait Decode<'a, T> { 
    fn decode_from(&'a mut self) -> T; 
} 

И сделать соответствующие изменения в реализацию

impl<'a> Decode<'a, MQTTFrame<'a>> for MQTTFrameDecoder<'a> { ... } 

Теперь, если мы попытаемся код выше (манеж is.gd/BLStYq), то заимствует шашка жалуется:

<anon>:28:9: 28:24 error: cannot borrow `*decoder.payload` as mutable more than once at a time [E0499] 
<anon>:28   decoder.payload.push(1); 

Это потому, что ссылка на decoder должна иметь срок службы 'a, если это необходимо для того, чтобы вызвать функцию decode_from. Прокомментируйте строку нарушения и остальную часть примера компиляции! Этот код теперь безопасен, поскольку не изменяются сроки жизни.


Помимо:

Как ссылка на decoder должна жить так долго, как сам декодер, вы не можете использовать decoder вообще после того, как вы назвали decode_from. В этом случае, возможно, лучше выразить это, приняв self вместо &'a mut self. Тогда синтаксис немного чище, и очевидно, что после использования декодера его нельзя использовать снова.

pub trait Decode<T> { 
    fn decode_from(self) -> T; 
} 

pub struct MQTTFrame<'a> { 
    pub payload: &'a Vec<u8>, 
} 

pub struct MQTTFrameDecoder<'a> { 
    pub payload: &'a mut Vec<u8>, 
} 

impl<'a> Decode<MQTTFrame<'a>> for MQTTFrameDecoder<'a> { 
    fn decode_from(self) -> MQTTFrame<'a> { 
     MQTTFrame{ payload: self.payload } 
    } 
} 
+0

Спасибо, Djzin, этот ответ имеет кольцо правды, хотя я не совсем понимаю его. Мне нужно время подумать об этом. Не удивляйтесь, если я вернусь к вам с большим количеством вопросов. Один из них у меня уже есть: если сообщество Rust говорит «изменчивая ссылка», действительно ли это означает «неизменную» ссылку на изменяемое значение »? –

+0

Я принял ваш ответ, потому что я считаю, что теперь я почти полностью понимаю ваши рассуждения: –

+0

Я принял ваш ответ, потому что я считаю, что теперь я почти полностью понимаю ваши рассуждения: ссылка на 'MQTTFrameDecoder' есть - по транзитивности - * изменяемая * ссылка на вектор 'полезная нагрузка'. Аналогично, ссылка на «MQTTFrame» является ссылкой на тот же вектор «полезная нагрузка». Представленный вами код приведет к расширению сферы охвата - к ситуации, когда изменяемые и различные неизменяемые ссылки - 'decoder' и' frame' - находятся в сфере охвата. Это запрещено. Верный? –

0

Lifetime elision работает только в очень простых случаях. Это делает их слабыми, но их легко объяснить (также простые случаи на удивление распространены).

Как только у вас есть общий параметр времени жизни, elision больше не применяется - компилятор отказывается угадать ваше намерение.

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