Returning this value requires that `'1` must outlive `'2`

Hey, I am trying to write an async method that takes a closure as an argument and applies it to self. Included a minimal example:

use futures::future::{self, BoxFuture};
use futures::Future;

#[derive(std::fmt::Debug, Clone)]
struct MyStruct {
    x: u32,
}

impl MyStruct {
    fn check<'a, F, T, FR>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        F: (FnOnce(&mut Self) -> FR) + Send + 'a,
        FR: Future<Output = T> + Send,
        T: Send,
    {
        Box::pin(async move { f(self).await })
    }

    fn func(&mut self) -> BoxFuture<'_, ()> {
        Box::pin(future::ready(()))
    }
}

fn main() {
    let mut my_struct = MyStruct { x: 0 };
    let _inner_fut = my_struct.check(|my_struct| async move {
        my_struct.func().await;
    });
}

I am using a single dependency, futures = "0.3.7"

$ rustc --version
rustc 1.47.0 (18bf6b4f0 2020-10-07)

This is the compilation error I get:

$ cargo run
   Compiling check_borrow2 v0.1.0 (/home/real/temp/check_borrow2)
error: lifetime may not live long enough
  --> src/main.rs:26:50
   |
26 |       let _inner_fut = my_struct.check(|my_struct| async move {
   |  _______________________________________----------_^
   | |                                       |        |
   | |                                       |        return type of closure is impl futures::Future
   | |                                       has type `&'1 mut MyStruct`
27 | |         my_struct.func().await;
28 | |     });
   | |_____^ returning this value requires that `'1` must outlive `'2`

error: aborting due to previous error

error: could not compile `check_borrow2`.

My guess is that the compiler thinks the return value form the closure somehow outlives the my_struct reference, but I have to admit I don't fully understand the compiler's error here. Who is lifetime '2?

As a further clarification, I am using BoxFuture and not impl Future here, because in the original code the method is inside a trait, and using impl Future tricks there is not possible yet.

I tried to play with the lifetimes, but couldn't figure it out. I also noticed that if the call to func() is omitted, the code seems to compile. Any help is appreciated!

On nightly, the error message is slightly better and explains '2:

return type of closure `impl futures::Future` contains a lifetime `'2`
1 Like

The problem is that your closure F returns a value that holds a reference.

F: (FnOnce(&mut Self) -> FR) + Send + 'a,
            ^            ^^
            |       your return value of type `FR` contains
            |       a reference with the same lifetime as
            \=======the first parameter

However, according to the way you've written the bound for F, there's no link between the lifetime of FR and &mut Self. Adjust your code as follows:


impl MyStruct {
    fn check<'a: 'b, 'b, F, T, FR>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        F: (FnOnce(&'b mut Self) -> FR) + Send + 'a,
        FR: Future<Output = T> + Send + 'b,
        T: Send,
    {
        Box::pin(async move { f(self).await })
    }

    fn func(&mut self) -> BoxFuture<'_, ()> {
        Box::pin(future::ready(()))
    }
}
1 Like

@jethrogb: Thanks!
I just recalled that I tried this approach, but it didn't work out because of other reasons.
I tried to reconstruct a minimal example that is more faithful to the original code:

use futures::future::{self, BoxFuture};
use futures::Future;
use std::mem;

#[derive(std::fmt::Debug, Clone)]
struct MyStruct {
    x: u32,
}

impl MyStruct {
    fn check<'a: 'b, 'b, F, T, FR>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        F: (FnOnce(&'b mut Self) -> FR) + Send + 'a,
        FR: Future<Output = T> + Send + 'b,
        T: Send,
    {
        let orig = self.clone();
        Box::pin(async move {
            let res = f(self).await;
            let _ = mem::replace(self, orig);
            res
        })
    }

    fn func(&mut self) -> BoxFuture<'_, ()> {
        Box::pin(future::ready(()))
    }
}

fn main() {
    let mut my_struct = MyStruct { x: 0 };
    let _inner_fut = my_struct.check(|my_struct| async move {
        my_struct.func().await;
    });
}

Now I get this compilation error:

$ cargo run
   Compiling check_borrow2 v0.1.0 (/home/real/temp/check_borrow2)
error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:20:34
   |
11 |     fn check<'a: 'b, 'b, F, T, FR>(&'a mut self, f: F) -> BoxFuture<'a, T>
   |                      -- lifetime `'b` defined here
...
19 |             let res = f(self).await;
   |                       -------
   |                       | |
   |                       | first mutable borrow occurs here
   |                       argument requires that `*self` is borrowed for `'b`
20 |             let _ = mem::replace(self, orig);
   |                                  ^^^^ second mutable borrow occurs here

Maybe what I'm trying to do here isn't really sound?

I tried one more thing and it seems to work, but I am not sure why (Code is included).
I made a compromise, and allowed the closure to return a BoxFuture<...> instead of the more generic impl Future.

use futures::future::{self, BoxFuture};
use std::mem;

#[derive(std::fmt::Debug, Clone)]
struct MyStruct {
    x: u32,
}

impl MyStruct {
    fn check<'a, F, T>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        for<'b> F: (FnOnce(&'b mut Self) -> BoxFuture<'b, T>) + Send + 'a,
        T: Send,
    {
        let orig = self.clone();
        Box::pin(async move {
            let res = f(self).await;
            let _ = mem::replace(self, orig);
            res
        })
    }

    fn func(&mut self) -> BoxFuture<'_, ()> {
        Box::pin(future::ready(()))
    }
}

fn main() {
    let mut my_struct = MyStruct { x: 0 };
    let _inner_fut = my_struct.check(|my_struct| {
        Box::pin(async move {
            my_struct.func().await;
        })
    });
}

In @jethrogb's code there was a bound: 'a: 'b, but it seems like it is not required here, and I am not sure why. I am also curious if this is solvable without having to use a BoxFuture<...> for the closure.

So it seems like my proposed solution is not usable in many cases. There is probably something wrong about the for<'b> ... bound. Here is an example:

use futures::future::{self, BoxFuture};
use std::mem;

#[derive(std::fmt::Debug, Clone)]
struct MyStruct {
    x: u32,
}

impl MyStruct {
    fn check<'a, F, T>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        for<'b> F: (FnOnce(&'b mut Self) -> BoxFuture<'b, T>) + Send + 'a,
        T: Send,
    {
        let orig = self.clone();
        Box::pin(async move {
            let res = f(self).await;
            let _ = mem::replace(self, orig);
            res
        })
    }

    fn func(&mut self) -> BoxFuture<'_, ()> {
        Box::pin(future::ready(()))
    }
}

fn invoke(my_struct: &mut MyStruct, other_struct: &MyStruct) {
    let _inner_fut = my_struct.check(|my_struct| {
        Box::pin(async move {
            my_struct.x += other_struct.x;
        })
    });
}

fn main() {
    let other_struct = MyStruct { x: 5 };
    let mut my_struct = MyStruct { x: 0 };
    invoke(&mut my_struct, &other_struct);
}

The compilation error here is:

$ cargo run
   Compiling check_borrow2 v0.1.0 (/home/real/temp/check_borrow2)
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:30:29
   |
30 |           Box::pin(async move {
   |  _____________________________^
31 | |             my_struct.x += other_struct.x;
32 | |         })
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the function body at 28:1...
  --> src/main.rs:28:1
   |
28 | fn invoke(my_struct: &mut MyStruct, other_struct: &MyStruct) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/main.rs:30:29
   |
30 |           Box::pin(async move {
   |  _____________________________^
31 | |             my_struct.x += other_struct.x;
32 | |         })
   | |_________^
   = note: expected `&MyStruct`
              found `&MyStruct`
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the body at 29:38...
  --> src/main.rs:29:38
   |
29 |       let _inner_fut = my_struct.check(|my_struct| {
   |  ______________________________________^
30 | |         Box::pin(async move {
31 | |             my_struct.x += other_struct.x;
32 | |         })
33 | |     });
   | |_____^
note: ...so that the expression is assignable
  --> src/main.rs:30:9
   |
30 | /         Box::pin(async move {
31 | |             my_struct.x += other_struct.x;
32 | |         })
   | |__________^
   = note: expected `std::pin::Pin<std::boxed::Box<dyn futures::Future<Output = ()> + std::marker::Send>>`
              found `std::pin::Pin<std::boxed::Box<dyn futures::Future<Output = ()> + std::marker::Send>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: could not compile `check_borrow2`.

To learn more, run the command again with --verbose.

For my future self, here is a workaround for the last issue:

use futures::executor::LocalPool;
use futures::future::BoxFuture;
use std::mem;

#[derive(std::fmt::Debug, Clone)]
struct MyStruct {
    x: u32,
}

trait CustomFunc<A, B> {
    fn call<'a>(&'a mut self, a: &'a mut A) -> BoxFuture<'a, B>;
}

impl MyStruct {
    fn check<'a, F, T>(&'a mut self, mut f: F) -> BoxFuture<'a, T>
    where
        F: CustomFunc<Self, T> + Send + 'a,
        T: Send,
    {
        let orig = self.clone();
        Box::pin(async move {
            let res = f.call(self).await;
            let _ = mem::replace(self, orig);
            res
        })
    }
}

struct Closure<'a> {
    other_struct: &'a MyStruct,
}

impl<'a> Closure<'a> {
    fn new(other_struct: &'a MyStruct) -> Closure {
        Closure { other_struct }
    }
}

impl<'b> CustomFunc<MyStruct, ()> for Closure<'b> {
    fn call<'a>(&'a mut self, my_struct: &'a mut MyStruct) -> BoxFuture<'a, ()> {
        Box::pin(async move {
            my_struct.x += self.other_struct.x;
        })
    }
}

fn invoke<'a>(my_struct: &'a mut MyStruct, other_struct: &'a MyStruct) -> BoxFuture<'a, ()> {
    let closure = Closure::new(other_struct);
    my_struct.check(closure)

}

fn main() {
    let other_struct = MyStruct { x: 5 };
    let mut my_struct = MyStruct { x: 0 };
    let my_future = invoke(&mut my_struct, &other_struct);
    LocalPool::new().run_until(my_future);
}

Not yet sure why this implementation works, but using an FnOnce or FnMut doesn't.

The problem is elsewhere. If instead of a function call, you create the reference in a scope in main:

fn main() {
    let other_struct = MyStruct { x: 5 };
    let mut my_struct = MyStruct { x: 0 };
    // invoke(&mut my_struct, &other_struct);
    {
        let ref_other_struct = &other_struct;
        my_struct.check(|my_struct| {
            Box::pin(async move {
                my_struct.x += ref_other_struct.x;
            })
        });
    }
}

(Link to playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=83be32e5c2e6d3a35f40d429efab3d8d )
You get a much clearer error:

  Compiling playground v0.0.1 (/playground)
error[E0597]: `other_struct` does not live long enough
  --> src/main.rs:41:32
   |
41 |           let ref_other_struct = &other_struct;
   |                                  ^^^^^^^^^^^^^ borrowed value does not live long enough
42 |           my_struct.check(|my_struct| {
43 | /             Box::pin(async move {
44 | |                 my_struct.x += ref_other_struct.x;
45 | |             })
   | |______________- returning this value requires that `other_struct` is borrowed for `'static`
...
48 |   }
   |   - `other_struct` dropped here while still borrowed

error: aborting due to previous error

Which points you in the direction of the right fix:

fn invoke(my_struct: &mut MyStruct, other_struct: &MyStruct) {
    let _inner_fut = my_struct.check(|my_struct| {
        let other_struct_x = other_struct.x;
        Box::pin(async move {
            my_struct.x += other_struct_x;
        })
    });
}

(Link to playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4b59d8d23585aa94e1bce9f1aafde7e2 )
The problem is that you're capturing the reference inside the future you're returning, so it has to live as long as that future, which can be indefinitely (since you're returning it).

The for<'b> bound is for making sure that you can run f(self) on a short re-borrow, which is all you need since you await it immediately. Essentially this is something like:

    fn check<'a, F, T>(&'a mut self, f: F) -> BoxFuture<'a, T>
    where
        for<'b> F: (FnOnce(&'b mut Self) -> BoxFuture<'b, T>) + Send + 'a,
        T: Send,
    {
        let orig = self.clone();
        Box::pin(async move {
            let res;
            {
                let tmp = &mut *self;
                res = f(tmp).await;
            }
            let _ = mem::replace(self, orig);
            res
        })
    }

In the other version (check<'a: 'b, 'b ...) this lifetime is fixed to the function, and then this doesn't work - I think because then it has to be longer than the function.

I'm not completely sure, but I think what's happening is this:
As I wrote above, the problem is that the reference has to live as long as the future you return in the function passed to check. The solution I suggested above is to get rid of the reference to other_struct and not pass it into the closure; but this is of course quite limiting.
The solution you suggest here is promising that the returned Future doesn't need to live longer than the reference (so that it's not a problem for it to hold on to it). But this bound is incompatible with the for<'b> bound, which needs to work also for 'b-s which are larger than the one of other_struct. As you only actually need to work for 'b-s which are smaller than the one of other_struct (and of 'a), there's no actual contradiction, and you can express the exact bound with the custom trait. So in a sense the problem is also with the for<'b> bound.

I've found two related questions, and it seems that it's neither necessary to define a custom trait nor to use boxing, provided one can use the nightly #![feature(type_alias_impl_trait)].