0

My struct ReadingState takes the function recv_dgram as argument in its new() method. recv_dgram takes as an argument a buffer with some lifetime 'r, and returns a Future of a certain type. The Item of this future contains the buffer that was fed as an argument, with the same lifetime 'r.

This is how ReadingState looks like:

struct FragMsgReceiver<'a, A, FUNC: 'a>
where
    FUNC: for<'r> FnMut(&'r [u8])
        -> Future<Item = (&'r [u8], usize, A), Error = io::Error>,
{
    frag_state_machine: FragStateMachine,
    recv_dgram: &'a mut FUNC,
    get_cur_instant: &'a mut FnMut() -> Instant,
}

struct ReadingState<'a, 'c, A, FUNC: 'a, F>
where
    F: Future<Item = (&'c mut [u8], usize, A), Error = io::Error>,
    FUNC: for<'r> FnMut(&'r [u8])
        -> Future<Item = (&'r [u8], usize, A), Error = io::Error>,
{
    frag_msg_receiver: FragMsgReceiver<'a, A, FUNC>,
    temp_buff: Vec<u8>,
    res_buff: &'c mut [u8],
    opt_read_future: Option<F>,
}

The return type of FUNC is not the same as F because they use different lifetimes.

The structs by themselves can compile, but I can not use them correctly in the rest of my code. For example, this happens when I try to call frag_msg_receiver.recv_dgram and assign the result to the field opt_read_future of ReadingState:

error[E0308]: match arms have incompatible types
  --> src/frag_msg_receiver.rs:80:30
   |
80 |               let mut fdgram = match mem::replace(&mut reading_state.opt_read_future, None) {
   |  ______________________________^
81 | |                 Some(read_future) => read_future,
82 | |                 None => (*reading_state.frag_msg_receiver.recv_dgram)(
83 | |                     &mut reading_state.temp_buff),
84 | |             };
   | |_____________^ expected type parameter, found trait frag_msg_receiver::futures::Future
   |
   = note: expected type `F`
              found type `frag_msg_receiver::futures::Future<Item=(&[u8], usize, A), Error=std::io::Error> + 'static`

A dream solution (this is not valid Rust code) will be something of the form:

struct ReadingState<'a, 'c, A, FUNC: 'a, F> 
    where for <'r> {
        F: Future<Item = (&'r mut [u8], usize, A), Error = io::Error>,
        FUNC: FnMut(&'r [u8]) -> F,
    }
{
    // ...
}

I don't know how to achieve this with the existing syntax.

Edit: I made the smallest possible self contained example I could, but it doesn't compile for possibly different reasons. I am including it here (playground):

trait MockFutureTrait {
    type Item;
    fn get_item(self) -> Self::Item;
}

type FnTraitObject = FnMut(&mut [u8]) -> MockFutureTrait<Item=&mut [u8]>;

struct MockFuture<T> {
    item: T,
}

impl<T> MockFutureTrait for MockFuture<T> {
    type Item=T;
    fn get_item(self) -> Self::Item {
        self.item
    }
}

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

struct RecvMsg<'a,'c,F>
    where F: MockFutureTrait<Item=&'c mut [u8]> {

    frag_msg_receiver: FragMsgReceiver<'a>,
    res_buff: &'c mut [u8],
    read_future: F,
}


fn main() {
    let mut recv_dgram = |buf: &mut [u8]| {
        MockFuture {
            item: buf,
        }
    };

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

The compilation error I get:

error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:33:26: 37:6] as std::ops::FnOnce<(&'r mut [u8],)>>::Output == MockFutureTrait<Item=&'r mut [u8]> + 'static`
  --> src/main.rs:40:21
   |
40 |         recv_dgram: &mut recv_dgram,
   |                     ^^^^^^^^^^^^^^^ expected struct `MockFuture`, found trait MockFutureTrait
   |
   = note: expected type `MockFuture<&mut [u8]>`
              found type `MockFutureTrait<Item=&mut [u8]> + 'static`
   = note: required for the cast to the object type `for<'r> std::ops::FnMut(&'r mut [u8]) -> MockFutureTrait<Item=&'r mut [u8]> + 'static + 'static`

error: aborting due to previous error

error: Could not compile `noncompiling_lifetime_trait`.

I'm not sure that I know what I'm doing, and why the compile problems changed. You might have an idea.

real
  • 639
  • 1
  • 6
  • 15
  • Are you saying you want the return value of `FUNC` to be the same as `F`? I'm not sure I can follow the question that is being asked unfortunately. Can you reduce this to a single example, preferebly something runnable in the [playground](http://play.rust-lang.org/)? – Timidger Sep 13 '17 at 21:30
  • Side note: your `mem::replace` on the `Option` can be recreated by using [take](https://doc.rust-lang.org/std/option/enum.Option.html#method.take) – Timidger Sep 13 '17 at 21:31
  • @Timidger: I didn't know about `take`. This is really cool, thank you. – real Sep 14 '17 at 19:44

1 Answers1

1

What you're hoping to do isn't possible as of Rust 1.20. You need generic associated types in order to bind the correct lifetime on the type parameter F. The solution would look like this (obviously, I can't test it because generic associated types are not implemented yet):

use std::marker::PhantomData;

trait FragFutureFamily<A> {
    type F<'a>: Future<Item = (&'a [u8], usize, A), Error = io::Error>;
}

struct FragMsgReceiver<'a, A, FUNC: 'a, FF>
where
    FF: FragFutureFamily<A>,
    FUNC: for<'r> FnMut(&'r [u8]) -> FF::F<'r>,
{
    frag_state_machine: FragStateMachine,
    recv_dgram: &'a mut FUNC,
    get_cur_instant: &'a mut FnMut() -> Instant,
    _phantom_future_family: PhantomData<FF>,
}

struct ReadingState<'a, 'c, A, FUNC: 'a, F, FF>
where
    F: Future<Item = (&'c mut [u8], usize, A), Error = io::Error>,
    FF: FragFutureFamily<A>,
    FUNC: for<'r> FnMut(&'r [u8]) -> FF::F<'r>,
{
    frag_msg_receiver: FragMsgReceiver<'a, A, FUNC, FF>,
    temp_buff: Vec<u8>,
    res_buff: &'c mut [u8],
    opt_read_future: Option<F>,
    _phantom_future_family: PhantomData<FF>,
}

Note: I left the F type parameter on ReadingState because the type is slightly different from FragFutureFamily::F, though if you can make the types agree, you could change the type of opt_read_future to Option<FF::F<'c>>.


As a workaround, you could use Box<Future<...>> instead of a type parameter for the Future type.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • I didn't know about PhantomData. I assume that it is used to add constraints for types, very interesting. I hoped that I will not need to use the Box<> workaround, as I want to avoid the allocation ): I added self contained code example, but it doesn't compile. – real Sep 14 '17 at 19:51
  • You can't have a function return a `MockFutureTrait`, because that's an unsized type. You need to return a concrete type that implements `MockFutureTrait`, but you can't specify a single concrete type that works for *every* lifetime. – Francis Gagné Sep 14 '17 at 23:36
  • My attempts to use Boxed idea could be found on [this question](https://stackoverflow.com/questions/46253306/lifetime-problems-for-a-function-returning-a-boxed-trait-that-contains-a-referen) – real Sep 16 '17 at 11:19