I am trying to figure out how to write an api that takes a context (&mut C
) and a future producing closure/fn. The closure should be called with a mutable reference to the context (&mut &mut C
) and produces a future containing that mutable reference to the context.
Some of my first attempts looked like this:
use core::future::Future;
struct AuthSession;
impl AuthSession {
fn app_id(&mut self) -> i32 {
1
}
}
trait InputCapturingFn<'a, I: 'a, O: 'a>: FnMut(&'a mut I) -> O {}
impl<'a, I: 'a, O: 'a, F: FnMut(&'a mut I) -> O> InputCapturingFn<'a, I, O> for F {}
fn retry_with_context<C, Fn, Fut>(
context: C,
mut operation: Fn,
) -> impl Future<Output = Result<(), ()>>
where
Fn: for<'c> InputCapturingFn<'c, C, Fut>,
Fut: Future<Output = ()>,
C: Send,
{
async move {
let mut context = context;
let _res = operation(&mut context).await;
let res = operation(&mut context).await;
Ok(res)
}
}
async fn test(auth_session: &mut AuthSession) {
retry_with_context(
auth_session,
|auth_session: &mut &mut AuthSession| async move {
let auth_session = auth_session;
let _ = auth_session.app_id();
},
)
.await;
}
The compiler errors with:
error: lifetime may not live long enough
--> src/lib.rs:40:47
|
40 | |auth_session: &mut &mut AuthSession| async move {
| ________________________-____________________-_^
| | | |
| | | return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| | let's call the lifetime of this reference `'1`
41 | | let auth_session = auth_session;
42 | | let _ = auth_session.app_id();
43 | | },
| |_________^ returning this value requires that `'1` must outlive `'2`
error: lifetime may not live long enough
--> src/lib.rs:40:47
|
40 | |auth_session: &mut &mut AuthSession| async move {
| _____________________________-_______________-_^
| | | |
| | | return type of closure `impl Future<Output = ()>` contains a lifetime `'4`
| | let's call the lifetime of this reference `'3`
41 | | let auth_session = auth_session;
42 | | let _ = auth_session.app_id();
43 | | },
| |_________^ returning this value requires that `'3` must outlive `'4`
|
= note: requirement occurs because of a mutable reference to `&mut AuthSession`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0521]: borrowed data escapes outside of function
--> src/lib.rs:38:5
|
37 | async fn test(auth_session: &mut AuthSession) {
| ------------ - let's call the lifetime of this reference `'1`
| |
| `auth_session` is a reference that is only valid in the function body
38 | / retry_with_context(
39 | | auth_session,
40 | | |auth_session: &mut &mut AuthSession| async move {
41 | | let auth_session = auth_session;
42 | | let _ = auth_session.app_id();
43 | | },
44 | | )
| | ^
| | |
| |_____`auth_session` escapes the function body here
| argument requires that `'1` must outlive `'static`
I was hoping InputCapturingFn<'a, I: 'a, O: 'a>: FnMut(&'a mut I) -> O
could've provided enough information that the output captures the input but regardless the compiler produces: returning this value requires that `'1` must outlive `'2`
.
What am I missing? Is there any way to express this pattern?