0

I am trying to compile the following code (playground):

trait MockFutureTrait {
    type Item;
}

struct MockFuture<T> {
    item: T,
}

impl<T> MockFutureTrait for MockFuture<T> {
    type Item = T;
}

struct FragMsgReceiver<'a, 'c: 'a> {
    recv_dgram: &'a FnMut(&mut [u8])
        -> Box<MockFutureTrait<Item = &mut [u8]> + 'c>,
}

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


fn main() {
    let mut recv_dgram = constrain_handler(|buf: &mut [u8]| {
        Box::new(MockFuture { item: buf }) as Box<MockFutureTrait<Item = &mut [u8]>>
    });

    let ref_recv_dgram = &mut recv_dgram;
    let fmr = FragMsgReceiver {
        recv_dgram: ref_recv_dgram,
    };
}

And I get the compile error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:28:37
   |
28 |         Box::new(MockFuture { item: buf }) as Box<MockFutureTrait<Item = &mut [u8]>>
   |                                     ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 27:44...
  --> src/main.rs:27:44
   |
27 |       let mut recv_dgram = constrain_handler(|buf: &mut [u8]| {
   |  ____________________________________________^
28 | |         Box::new(MockFuture { item: buf }) as Box<MockFutureTrait<Item = &mut [u8]>>
29 | |     });
   | |_____^
note: ...so that expression is assignable (expected &mut [u8], found &mut [u8])
  --> src/main.rs:28:37
   |
28 |         Box::new(MockFuture { item: buf }) as Box<MockFutureTrait<Item = &mut [u8]>>
   |                                     ^^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<MockFutureTrait<Item=&mut [u8]> + 'static>, found std::boxed::Box<MockFutureTrait<Item=&mut [u8]>>)
  --> src/main.rs:28:9
   |
28 |         Box::new(MockFuture { item: buf }) as Box<MockFutureTrait<Item = &mut [u8]>>
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I tried to add various lifetime hints, but I couldn't get this code to compile.

My previous related questions on SO about this:

Note that I am using the helper function constrain_handler according to a suggestion I got in question 2; it allows me to overcome a different compilation error.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
real
  • 639
  • 1
  • 6
  • 15
  • I never saw an answer to [my comment on your original question](https://stackoverflow.com/questions/46194930/lifetime-problems-with-struct-containing-function-reference#comment79358569_46194930) about why you want this to be a reference to a closure anyway. – Shepmaster Sep 16 '17 at 13:25
  • @shepmaster: Is there another way to do this? What I want to do is take a datagram based socket, and wrap it in a new type that will allow me to send and receive abstract datagrams (There is no 1-1 correspondence between those and the underlying datagrams). Therefore I take a reference to the `recv_dgram` function. – real Sep 16 '17 at 13:42

1 Answers1

1

It appears that you've missed a key takeaway of your previous questions and their duplicates:

By declaring a type on the closure argument, you stop performing type inference for the arguments. This causes a new implicit lifetime to be generated by the closure, one which does not match your requirements. Just don't declare the type at all.

Next, you need to state that your closure is going to take a reference to some bytes and return a boxed trait object that will return some bytes of the same lifetime and contains a reference of that same lifetime:

struct FragMsgReceiver<'a> {
    recv_dgram: &'a for<'b> FnMut(&'b mut [u8])
        -> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
}

See Why is Box<Iterator<Item = &Foo> + 'a> needed? for more details about the + 'a syntax.

Then update constrain_handler to match:

struct FragMsgReceiver<'a> {
    recv_dgram: &'a for<'b> FnMut(&'b mut [u8])
        -> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
}

fn constrain_handler<F>(f: F) -> F
where
    F: for<'b> FnMut(&'b mut [u8])
        -> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
{
    f
}

fn main() {
    let mut recv_dgram = constrain_handler(|buf| Box::new(MockFuture { item: buf }));

    let fmr = FragMsgReceiver {
        recv_dgram: &mut recv_dgram,
    };
}

The whole thing can be made simpler if you just take a generic closure directly:

struct FragMsgReceiver<R>
where
    R: for<'b> FnMut(&'b mut [u8])
        -> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
{
    recv_dgram: R,
}

fn main() {
    let fmr = FragMsgReceiver {
        recv_dgram: |buf| Box::new(MockFuture { item: buf }),
    };
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Is there a difference between your first definition of `FragMsgReceiver` and the second one (Where you use `R` in the where clause)? – real Sep 16 '17 at 13:58
  • @real yes — it's no longer a reference to a trait object (note missing `&'a`) but instead is a generic type that will be monomorphized. – Shepmaster Sep 16 '17 at 14:02
  • I didn't know this was possible. So now the size of FragMsgReceiver depends on the size of the closure? Is the whole closure stored inside FragMsgReceiver? – real Sep 16 '17 at 14:13
  • @real *the size of `FragMsgReceiver` depends on the size of the closure* — yes, which [would be zero in this case](https://gist.github.com/ac3265f7f24c7b270993fcb80d80aa76). *the whole closure stored inside `FragMsgReceiver`* — yes, `FragMsgReceiver` owns the closure. – Shepmaster Sep 16 '17 at 14:46
  • @real If I may ask, what route of learning had you learn about references to trait objects *before* learning about generics? – Shepmaster Sep 16 '17 at 14:47
  • I read the Rust book from from beginning to end, and then Rust by Example. I have the background of Assembly and C programming. In C you can only store a pointer to a function, you can't store the function itself inside a structure. It is very strange for me that the resulting structure is of size 0. Can you recommend anything else to read about this? – real Sep 17 '17 at 04:17
  • To reiterate, [a closure is not a function](https://doc.rust-lang.org/stable/book/second-edition/ch13-01-closures.html); C doesn't have closures (C++ does now though). In Rust, you can also store a function pointer, but that's not what is happening here. The compiler conceptually makes a copy of all the code that has a generic parameter and "inlines" the concrete type. Since the closure doesn't capture anything, the struct doesn't have anything useful to store at runtime, so it's emptied out. – Shepmaster Sep 17 '17 at 13:42