How can multiple struct fields be generics that us

2019-08-24 10:33发布

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.

标签: rust lifetime
1条回答
一纸荒年 Trace。
2楼-- · 2019-08-24 11:35

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.

查看更多
登录 后发表回答