简体   繁体   English

为什么我的特质需要一个终身参数?

[英]Why does my trait need a lifetime parameter?

Being a Rust newbie, I probably somewhat naively started with this: 作为一个Rust新手,我可能有点天真地开始这个:

...

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 }
    }
}

Which, when trying to compile, was greeted with: 当试图编译时,迎合了:

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     }

Somewhere on StackOverflow - sorry, I forgot where - someone in a similar case suggested to add a lifetime parameter like so (omitting unchanged code): 在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 }
    }
}

And lo and behold! 瞧,看哪! It compiles. 它汇编。 Now if I could only understand why it compiles. 现在,如果我只能理解它为什么编译。 Could someone explain 有人可以解释一下

  1. why the original code did not compile? 为什么原始代码没有编译?
  2. why the modified code compiles? 为什么修改后的代码编译?

Here's a reduced testcase that fails to compile ( playpen ): 这是一个无法编译的简化测试用例(游戏围栏 ):

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 }
    }
}

Note that I have elided the lifetimes for the decode_from function and removed the redundant stream parameter. 请注意,我已经省略了 decode_from函数的生命周期并删除了冗余流参数。

It is clear then that the function is taking a reference with an arbitrarily short lifetime 'b , and then extending it to have lifetime 'a . 很明显,该函数采用任意短的寿命'b ,然后将其扩展为具有生命周期'a This is a problem with mutable references as then you can borrow something mutably and immutably at the same time: 这是可变引用的一个问题,因为你可以在同一时间可变地和不可变地借用一些东西:

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);
    }
}

For this reason the borrow checker refuses to let the function compile. 因此借用检查器拒绝让函数编译。

If you force the reference to decoder to have lifetime 'a , though, then there is no longer a problem. 但是,如果强制对decoder的引用具有生命周期'a ,则不再存在问题。 The compiler cannot use the reference with the shorter lifetime, it must mutably borrow decoder for longer, and so the compiler should give us an error when we try to borrow it again. 编译器不能使用寿命较短的引用,它必须可变地借用decoder更长时间,因此当我们再次尝试借用它时,编译器应该给我们一个错误。

In order to achieve this, we would like to write 为了达到这个目的,我们想写一下

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

But now we get an error: 但现在我们得到一个错误:

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

To fix this, we need to have our trait be aware that you can only decode_from certain lifetimes, not arbitrary ones. 要解决这个问题,我们需要让我们的特性意识到你只能decode_from某些生命周期,而不是任意生命周期。 So change decode to 所以改变解码为

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

And make the appropriate change to the implementation 并对实施进行适当的更改

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

Now if we try the code above (playpen is.gd/BLStYq), the borrow checker complains: 现在,如果我们尝试上面的代码(playpen 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);

That's because, now, the reference to decoder must have lifetime 'a when it is taken in order to call the function decode_from . 那是因为,现在,对decoder的引用必须具有生命周期'a才能调用函数decode_from Comment out the offending line and the rest of the example compiles! 注释掉有问题的行,其余的示例编译! This code is now safe because no mutable lifetimes are being extended. 此代码现在是安全的,因为没有延长可变的生命周期。


Aside: 在旁边:

As the reference to decoder must live as long as the decoder itself, you can't actually use decoder at all after you have called decode_from . 由于对decoder的引用必须与解码器本身一样长,所以在调用decode_from之后根本不能使用decoder As this is the case, it may be better to express this by taking self instead of &'a mut self . 在这种情况下,最好通过self而不是&'a mut self来表达这一点。 Then the syntax is a little cleaner, and it is obvious that once a decoder has been used then it can't be used again. 然后语法更清晰,很明显,一旦使用了解码器,就不能再使用它了。

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 }
    }
}

Lifetime elision works only in very simple cases. 终身省略仅适用于非常简单的情况。 This makes them weak, but easy to explain (also simple cases are surprisingly common). 这使它们变得脆弱,但很容易解释(简单的情况也很常见)。

As soon as you have a generic lifetime parameter, elision no longer applies – the compiler refuses to guess your intention. 只要你有一个通用的生命周期参数,elision就不再适用了 - 编译器拒绝猜测你的意图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM