'How to bind lifetimes of Futures to fn arguments in Rust

Im trying to write a simple run_transaction function for the Rust MongoDB Driver

This function tries to execute a transaction through the mongo db client and retries the transaction if it encounters a retryable error

Here is a minimum reproducible example of the function.

use mongodb::{Client, Collection, ClientSession};
use mongodb::bson::Document;
use std::future::Future;

pub enum Never {}

fn main() {
    run_transaction(|mut session| async move {
        let document = collection().find_one_with_session(None,  None, &mut session).await?.unwrap();
        let r: Result<Document, TransactionError<Never>> = Ok(document);
        return r;
    });
}

fn collection() -> Collection<Document> {
    unimplemented!();
}

fn client() -> Client {
    unimplemented!();
}

pub enum TransactionError<E> {
    Mongodb(mongodb::error::Error),
    Custom(E)
}

impl<T> From<mongodb::error::Error> for TransactionError<T> {
    fn from(e: mongodb::error::Error) -> Self {
        TransactionError::Mongodb(e)
    }
}

// declaration
pub async fn run_transaction<T, E, F, Fut>(f: F) -> Result<T, TransactionError<E>> 
  where for<'a>
        F: Fn(&'a mut ClientSession) -> Fut + 'a,
        Fut: Future<Output = Result<T, TransactionError<E>>> { 

  
  let mut session = client().start_session(None).await?;
  session.start_transaction(None).await?;
      
  'run: loop {
    let r = f(&mut session).await;

    match r {
      Err(e) => match e {
        TransactionError::Custom(e) => return Err(TransactionError::Custom(e)),
        TransactionError::Mongodb(e) => {
          if !e.contains_label(mongodb::error::TRANSIENT_TRANSACTION_ERROR) {
            return Err(TransactionError::Mongodb(e));
          } else {
            continue 'run;
          }
        }
      },

      Ok(v) => {
        'commit: loop {
          match session.commit_transaction().await {
            Ok(()) => return Ok(v),
            Err(e) => {
              if e.contains_label(mongodb::error::UNKNOWN_TRANSACTION_COMMIT_RESULT) {
                continue 'commit;
              } else {
                return Err(TransactionError::Mongodb(e))
              }
            }
          }
        }
      }
    }
  }
}

But the borrow checker keeps complaining with this message:

error: lifetime may not live long enough
  --> src/main.rs:8:35
   |
8  |       run_transaction(|mut session| async move {
   |  ______________________------------_^
   | |                      |          |
   | |                      |          return type of closure `impl Future` contains a lifetime `'2`
   | |                      has type `&'1 mut ClientSession`
9  | |         let document = collection().find_one_with_session(None,  None, &mut session).await?.unwrap();
10 | |         let r: Result<Document, TransactionError<Never>> = Ok(document);
11 | |         return r;
12 | |     });
   | |_____^ returning this value requires that `'1` must outlive `'2`

Is there a way I can solve this?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source