问题
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.
回答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.
来源:https://stackoverflow.com/questions/46202681/how-can-multiple-struct-fields-be-generics-that-use-the-same-higher-kinded-lifet