Cannot infer a lifetime for a struct containing a

2019-08-02 17:26发布

问题:

This question already has an answer here:

  • Lifetime annotation for closure argument 2 answers
  • How to declare a lifetime for a closure argument? 3 answers

I am trying to make this simplified and self-contained version of my code compile:

struct FragMsgReceiver<'a, 'b: 'a> {
    recv_dgram: &'a mut FnMut(&mut [u8]) -> Result<&'b mut [u8], ()>,
}

impl<'a, 'b> FragMsgReceiver<'a, 'b> {
    fn new(
        recv_dgram: &'a mut FnMut(&mut [u8])
            -> Result<&'b mut [u8], ()>
    ) -> Self {
        FragMsgReceiver { recv_dgram }
    }
}

fn main() {
    let recv_dgram = |buff: &mut [u8]| Ok(buff);
    let fmr = FragMsgReceiver::new(&mut recv_dgram);
}

Here is the error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:15:43
   |
15 |     let recv_dgram = |buff: &mut [u8]| Ok(buff);
   |                                           ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 15:22...
  --> src/main.rs:15:22
   |
15 |     let recv_dgram = |buff: &mut [u8]| Ok(buff);
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &mut [u8], found &mut [u8])
  --> src/main.rs:15:43
   |
15 |     let recv_dgram = |buff: &mut [u8]| Ok(buff);
   |                                           ^^^^
note: but, the lifetime must be valid for the block suffix following statement 1 at 16:53...
  --> src/main.rs:16:53
   |
16 |       let fmr = FragMsgReceiver::new(&mut recv_dgram);
   |  _____________________________________________________^
17 | | }
   | |_^
note: ...so that variable is valid at time of its declaration
  --> src/main.rs:16:9
   |
16 |     let fmr = FragMsgReceiver::new(&mut recv_dgram);
   |         ^^^

From what I understand from the error message, the compiler doesn't understand that the buff reference (argument of recv_dgram) can actually live longer than the inner body of recv_dgram. I could be wrong though.

To give some context, I'm trying to create a struct that wraps a Rust Tokio UDP socket. To do this, I take a reference to a function recv_dgram. In my original code this function takes a buffer as argument, and returns a Future. When the Future is ready, the buffer will be filled. The Future's item also contains the address of sender and the amount of bytes that were written into the buffer.

回答1:

Let's start by restoring elided lifetime in your declaration

struct FragMsgReceiver<'a, 'b: 'a> {
    recv_dgram: &'a mut for<'c> FnMut(&'c mut [u8]) -> Result<&'b mut [u8], ()>,
}

This declaration means that FragMsgReceiver holds a mutable reference to a FnMut trait object which takes a mutable reference to a slice having any lifetime 'c and returns a reference with lifetime 'b: 'a.

This is not what you need. You need an FnMut which returns a reference with the same lifetime as the lifetime of the input parameter. This can be written as:

type FnTraitObject = FnMut(&mut [u8]) -> Result<&mut [u8], ()>;

struct FragMsgReceiver<'a> {
    recv_dgram: &'a mut FnTraitObject,
}

impl<'a> FragMsgReceiver<'a> {
    fn new(recv_dgram: &'a mut FnTraitObject) -> Self {
        FragMsgReceiver { recv_dgram }
    }
}

Lifetime elision does the right thing here, but the compiler still complains: "expected bound lifetime parameter, found concrete lifetime", pointing at FragMsgReceiver::new(&mut recv_dgram).

This error is caused by a limitation of Rust's type inference. We need to assist the inference by coercing the type of the closure as it was pointed out by DK.

fn constrain_handler<F>(f: F) -> F
where
    F: FnMut(&mut [u8]) -> Result<&mut [u8], ()>,
{
    f
}

// ...

let mut recv_dgram = constrain_handler(|buff| Ok(buff));

Complete code on the playground

To clarify, for<'c> means that 'c can be any lifetime and the lifetime is determined at a call site. For example, the pointer to the function fn foo(_: &u32) -> &u32 has the type for<'a> fn(&'a u32) -> &'a u32.



标签: rust lifetime