无法推断出适当的生命周期:向期货借用可变缓冲

时间:2017-09-21 13:08:28

标签: rust lifetime

我实现了具有以下高级逻辑的Tokio Future

  1. 拨打电话recv_dgram。这应该返回一个Future,当收到一个小数据报时它就准备好了。
  2. Future准备就绪时,请保留小数据报。
  3. 如果我们有足够的小数据报来重建大数据报,请将Future设置为就绪并返回重建的数据报。否则,请返回步骤1.
  4. 我有一些我无法理解的终身问题。我创建了一个自包含的简化代码来演示它。

    在下面的代码中,RecvMsg是在收到足够的小数据报时准备好的Futurerecv_dgram是一个函数,它返回一个Future,当一个小数据报到达时它就绪。

    我正在尝试编译以下代码(playground):

    extern crate futures;
    use self::futures::{Future, Poll, Async};
    
    struct RecvMsg<'d,R>
    where 
        R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>,
    {
        recv_dgram: R,
        temp_buff: Vec<u8>,
        opt_read_future: Option<Box<Future<Item=&'d mut [u8], Error=()> + 'd>>,
    }
    
    impl<'d,R> Future for RecvMsg<'d,R>
    where 
        R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>,
    {
        type Item = ();
        type Error = ();
    
        fn poll(&mut self) -> Poll<Self::Item, ()> {
    
            // Obtain a future datagram, 
            let mut fdgram = (self.recv_dgram)(&mut self.temp_buff);
            // Code compiles if this line is commented out:
            self.opt_read_future = Some(fdgram);
            return Ok(Async::NotReady);
        }
    }
    
    fn main() {}
    

    这是我收到的错误消息:

    error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
      --> src/main.rs:25:44
       |
    25 |         let mut fdgram = (self.recv_dgram)(&mut self.temp_buff);
       |                                            ^^^^^^^^^^^^^^^^^^^
       |
    note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 22:5...
      --> src/main.rs:22:5
       |
    22 | /     fn poll(&mut self) -> Poll<Self::Item, ()> {
    23 | |
    24 | |         // Obtain a future datagram, 
    25 | |         let mut fdgram = (self.recv_dgram)(&mut self.temp_buff);
    ...  |
    28 | |         return Ok(Async::NotReady);
    29 | |     }
       | |_____^
    note: ...so that reference does not outlive borrowed content
      --> src/main.rs:25:44
       |
    25 |         let mut fdgram = (self.recv_dgram)(&mut self.temp_buff);
       |                                            ^^^^^^^^^^^^^^^^^^^
    note: but, the lifetime must be valid for the lifetime 'd as defined on the impl at 13:1...
      --> src/main.rs:13:1
       |
    13 | / impl<'d,R> Future for RecvMsg<'d,R>
    14 | | where 
    15 | |     R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>,
    16 | | {
    ...  |
    29 | |     }
    30 | | }
       | |_^
    note: ...so that expression is assignable (expected std::option::Option<std::boxed::Box<futures::Future<Error=(), Item=&'d mut [u8]> + 'd>>, found std::option::Option<std::boxed::Box<futures::Future<Error=(), Item=&mut [u8]>>>)
      --> src/main.rs:27:32
       |
    27 |         self.opt_read_future = Some(fdgram);
       |                                ^^^^^^^^^^^^
    

    我对可能出现的问题有一些看法。我知道如果我注释掉这一行:

    reading_state.opt_read_future = Some(fdgram);
    

    代码编译成功。另外,我怀疑这里使用reading_state.temp_buff作为参数:

    let mut fdgram = (reading_state.frag_msg_receiver.recv_dgram)(
                    &mut reading_state.temp_buff);
    

    与问题有关。 (另见Why can't I store a value and a reference to that value in the same struct?

1 个答案:

答案 0 :(得分:0)

实现Future但没有'static生命周期的类型非常无用,因为事件循环无法运行它们。

此外,我认为Future::Item的生命周期很糟糕;我没有看到类型系统如何帮助您在控制流程中确定未来实际完成以释放生命周期。

所以你可能希望R: FnMut(Vec<u8>) -> Box<Future<Item=Vec<u8>, Error=()>>代替recv_dgram函数。它将接收一个缓冲区,它应该将新接收的数据附加到(它可以使用reserve()和“不安全”缓冲区填充+ set_len()进行优化。您还可以将Vec包装在某种自定义类型中以强制执行“仅附加”语义。

现在你应该意识到可变性不会对这个功能有很大的帮助 - 你可以连续10次调用它而无需等待返回的期货完成。相反,您可能希望传递显式状态。还可以避免装箱,并接受任何可转换为IntoFuture的结果:

// `S` is the state, `F` something convertible to a future `F::Future`.
pub struct RecvMsg<R, F, S>
where
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>,
    R: FnMut(S, Vec<u8>) -> F,
{
    recv_dgram: R,
    opt_read_future: Option<F::Future>,
}

当前挂起的读取操作现在变为Option<F::Future>,缓冲区仍处于该挂起操作中。

只要您的包装器被轮询(并且您尚未完成),就需要轮询此挂起的读取操作!

总而言之,它看起来像这样:

Playground

extern crate futures;
use self::futures::{IntoFuture,Future, Poll, Async};

pub struct RecvMsg<R, F, S>
where
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>,
    R: FnMut(S, Vec<u8>) -> F,
{
    recv_dgram: R,
    pending: Option<F::Future>,
}

impl<R, F, S> RecvMsg <R, F, S>
where
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>,
    R: FnMut(S, Vec<u8>) -> F,
{
    pub fn new(mut recv_dgram: R, initial_state: S) -> Self {
        let start = recv_dgram(initial_state, Vec::new()).into_future();
        RecvMsg{
            recv_dgram: recv_dgram,
            pending: Some(start)
        }
    }
}

impl<R, F, S> Future for RecvMsg <R, F, S>
where
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>,
    R: FnMut(S, Vec<u8>) -> F,
{
    type Item = Vec<u8>;
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.pending.take() {
            Some(mut p) => {
                match p.poll()? {
                    Async::Ready((state, buf)) => {
                        if buf.len() > 1024 {
                            // enough data
                            Ok(Async::Ready(buf))
                        } else {
                            // not enough data, read more
                            let next = (self.recv_dgram)(state, buf).into_future();
                            self.pending = Some(next);
                            Ok(Async::NotReady)
                        }
                    },
                    Async::NotReady => {
                        // still waiting for more data
                        self.pending = Some(p);
                        Ok(Async::NotReady)
                    },
                }
            },
            None => Ok(Async::NotReady), // already completed
        }
    }
}