Setting lifetimes for a function that returns a trait


#1

I have a function called recv_dgram that takes as input &mut [u8] and returns a Future with Item &mut [u8]. I would like to somehow define this function so that the lifetimes of the input buffer and the output buffer are the same.

I made a self contained code (below, playground) that presents this problem. Instead of Future we have MockFutureTrait. I manage to compile FragMsgReceiver below, where the output of the fuction is the struct MockFuture. However, I can’t manage to make FragMsgReceiver2 compile, where the output of the function is a type that satisfies the trait MockFutureTrait.

trait MockFutureTrait {
    type Item;
}

struct MockFuture<T> {
    item: T,
}

// This struct compiles:
struct FragMsgReceiver<'a> {
    recv_dgram: &'a FnMut(&mut [u8]) -> MockFuture<&mut [u8]>,
}

struct FragMsgReceiver2<'a,F>
    where F: MockFutureTrait<Item=&mut [u8]> {  // <-- Expected lifetime parameter here.
    recv_dgram: &'a FnMut(&mut [u8]) -> F,
}

fn main() {}

It is important for me that the lifetime of &mut [u8] is not part of the type of FragMsgReceiver2, because I want the function recv_dgram to be able to get various kinds of arguments with different lifetimes.

For completeness I include some of my previous attempts:

  • 1 [stackoverflow] I got the advice about how to write FragMsgReceiver correctly.
  • 2 [stackoverflow] it was suggested that I might not be able to do what I want with FragMsgReceiver2, unless I use ‘Generic associated types’, or I Box something.
  • 3 [stackoverflow] Attempt to solve this problem with Box<Trait<..>>. I still can’t manage to make it compile.
  • 4 [github] Compiler crash when trying to add for<'r>... syntax to solve the compilation problem.

I appreciate any ideas of how to approach this.


#2

How about this: https://play.rust-lang.org/?gist=1b09e0b8a77da0f22c3f020679f71983&version=stable

I think associated type constructors would obviate the need for a lifetime parameter on the trait as you could place a lifetime parameter on the associated type instead but I’ve not thought that through too hard.


#3

Hi @vitalyd, you always show up when I get in serious trouble.
Great thanks for this reply. I tried the gist that you have sent, it seems so simple and yet it compiles. I still don’t fully understand what you did there with MockFutureTrait<'a>.

I need to check this with the rest of my real code, hopefully I can tell you soon how it worked for me.


#4

@vitalyd: I tried to use the above suggestion in my code, but it seems like it doesn’t work well for me.

Here is a self contained example (playground):

trait MockFutureTrait<'b> {
    type Item;
}

struct MockFuture<T> {
    item: T,
}

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

struct FragMsgReceiver<'a,FUT>
    where FUT: for<'b> MockFutureTrait<'b,Item=&'b mut [u8]> + 'a {

    recv_dgram: &'a FnMut(&mut [u8]) -> FUT
}

fn main() {
    let mut recv_dgram = |buf| {
        MockFuture {
            item: buf,
        } 
    };
    let fmr = FragMsgReceiver {
        recv_dgram: &mut recv_dgram,
    };
}

I get the following compile errors:

error[E0281]: type mismatch: `[closure@src/main.rs:20:26: 24:6]` implements the trait `std::ops::FnMut<(_,)>`, but the trait `for<'r> std::ops::FnMut<(&'r mut [u8],)>` is required
  --> src/main.rs:26:21
   |
20 |       let mut recv_dgram = |buf| {
   |  __________________________-
21 | |         MockFuture {
22 | |             item: buf,
23 | |         } 
24 | |     };
   | |_____- implements `std::ops::FnMut<(_,)>`
25 |       let fmr = FragMsgReceiver {
26 |           recv_dgram: &mut recv_dgram,
   |                       ^^^^^^^^^^^^^^^
   |                       |
   |                       requires `for<'r> std::ops::FnMut<(&'r mut [u8],)>`
   |                       expected concrete lifetime, found bound lifetime parameter
   |
   = note: required for the cast to the object type `for<'r> std::ops::FnMut(&'r mut [u8]) -> _`

error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:20:26: 24:6] as std::ops::FnOnce<(&'r mut [u8],)>>::Output == _`
  --> src/main.rs:26:21
   |
26 |         recv_dgram: &mut recv_dgram,
   |                     ^^^^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#8r
   = note: required for the cast to the object type `for<'r> std::ops::FnMut(&'r mut [u8]) -> MockFuture<_>`

error: aborting due to 2 previous errors

In addition, I think that this line from the code suggestion:

where F: for<'b> MockFutureTrait<'b, Item=&'b mut [u8]> + 'a {

will not match what I’m trying to do, because I think the the return value of the FnMut output should have lifetime unrelated to lifetime of the FnMut reference.


#5

Yeah, I thought it probably wouldn’t work in use, despite compiling. I think it boils down to there not being a way to satisfy the HRTB bound for any given MockFutureTrait impl. So it seems like expressing this with generics would require associated type constructors since you’d then be able to place the lifetime parameter on precisely the right place.

I guess a workaround would be to box up the MockFutureTrait for now :frowning:.


#6

I will probably Box the MockFutureTrait as a workaround for now.
Again, thank you for helping me out!