rust - 为什么我的特征需要生命周期参数?

标签 rust lifetime

作为一个 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 }
    }
}

在尝试编译时,遇到了:

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. 为什么修改后的代码可以编译?

最佳答案

这是一个编译失败的精简测试用例 (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 }
    }
}

请注意,我有 elided the lifetimes对于 decode_from函数并删除了多余的流参数。

很明显,该函数正在使用任意短生命周期的引用 'b ,然后将其扩展为具有生命周期 'a .这是可变引用的问题,因为你可以同时以可变和不可变的方式借用一些东西:

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]

为了解决这个问题,我们需要让我们的 trait 知道你只能 decode_from某些生命,而不是任意的生命。所以将解码更改为

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

并对实现进行适当的更改

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

现在如果我们尝试上面的代码 (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);

那是因为,现在,对 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 }
    }
}

关于rust - 为什么我的特征需要生命周期参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36117966/

相关文章:

rust - 如何从actix SyncContext向另一个参与者发送消息?

data-structures - 如何从 cons 列表中弹出一个值?

c++ - 通过 const 获取参数并通过 const 返回的函数的生命周期延长

rust - 生命周期关闭捕获使用rust

rust - 当 Rust 结构包含生命周期特征时会发生什么?

linux - 为什么 Valgrind 没有显示 Rust 程序的分配?

rust - 如何创建一个新的空树

rust - 如何使用 winit rust crate 中的 raw_window_handle() 方法?

closures - 如何为闭包参数指定生命周期?

rust - 添加不相关的通用参数会触发奇怪的生命周期错误