Returning this value requires that '1 must outlive '2

Hello,

The context: I'd like to let user code define a closure that can execute operations on an LDAP server. I want to hide the opening/closing of the underlying connection.

Unfortunately, I can't make my code compile because I'm hitting some lifetime issues and I don't know how to resolve them.

use crate::{error::Error, CrateResult};
use ldap3::{Ldap, LdapConnAsync, LdapConnSettings, Scope, SearchEntry};
use std::future::Future;

struct LdapQuery {
    ldap: Ldap,
}

impl<'a> LdapQuery {
    async fn create_connection() -> CrateResult<Ldap> {
        // ...
        Ok(ldap)
    }

    pub async fn get_email(&mut self, username: &str) -> CrateResult<String> {
        // ...
        Ok("user@email.io")
    }

    pub async fn execute<T, F, Fut>(f: F) -> CrateResult<T>
    where
        F: FnOnce(&Self) -> Fut,
        Fut: Future<Output = CrateResult<T>>,
    {
        let mut ldap = Self::create_connection().await.unwrap();
        let result = f(&Self { ldap: ldap.clone() }).await;
        ldap.unbind().await.unwrap();
        result
    }
}

#[cfg(test)]
mod test {

    use super::*;

    #[tokio::test]
    async fn ldap_closure() -> CrateResult<()> {
        dotenv::dotenv().ok();

        let result = LdapQuery::execute(|x| async {
            let mut emails = Vec::new();
            emails.push(x.get_email("user_1").await.unwrap());
            emails.push(x.get_email("user_2").await.unwrap());
            Ok(emails)
        })
        .await
        .unwrap();

        println!("result: {:?}", result);

        Ok(())
    }
}

The current error is returning this value requires that '1 must outlive '2. I tried to annotate execute (wrongly), but to no avail.

Hi, it’s probably easier to help you if you posted the full error message (as printed by cargo check).

Sure, my bad :

error[E0596]: cannot borrow `*q` as mutable, as it is behind a `&` reference
  --> src/ldap.rs:91:25
   |
91 |             emails.push(q.get_email("user_1").await.unwrap());
   |                         ^ cannot borrow as mutable

error[E0596]: cannot borrow `*q` as mutable, as it is behind a `&` reference
  --> src/ldap.rs:92:25
   |
92 |             emails.push(q.get_email("user_2").await.unwrap());
   |                         ^ cannot borrow as mutable

error: lifetime may not live long enough
  --> src/ldap.rs:89:45
   |
89 |           let result = LdapQuery::execute(|q| async {
   |  __________________________________________--_^
   | |                                          ||
   | |                                          |return type of closure `impl warp::Future` contains a lifetime `'2`
   | |                                          has type `&'1 ldap::LdapQuery`
90 | |             let mut emails = Vec::new();
91 | |             emails.push(q.get_email("user_1").await.unwrap());
92 | |             emails.push(q.get_email("user_2").await.unwrap());
93 | |             Ok(emails)
94 | |         })
   | |_________^ returning this value requires that `'1` must outlive `'2`
   |
   = note: requirement occurs because of a mutable reference to &ldap::LdapQuery
   = 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[E0373]: async block may outlive the current function, but it borrows `q`, which is owned by the current function
  --> src/ldap.rs:89:51
   |
89 |           let result = LdapQuery::execute(|q| async {
   |  ___________________________________________________^
90 | |             let mut emails = Vec::new();
91 | |             emails.push(q.get_email("user_1").await.unwrap());
   | |                         - `q` is borrowed here
92 | |             emails.push(q.get_email("user_2").await.unwrap());
93 | |             Ok(emails)
94 | |         })
   | |_________^ may outlive borrowed value `q`
   |
note: async block is returned here
  --> src/ldap.rs:89:45
   |
89 |           let result = LdapQuery::execute(|q| async {
   |  _____________________________________________^
90 | |             let mut emails = Vec::new();
91 | |             emails.push(q.get_email("user_1").await.unwrap());
92 | |             emails.push(q.get_email("user_2").await.unwrap());
93 | |             Ok(emails)
94 | |         })
   | |_________^
help: to force the async block to take ownership of `q` (and any other referenced variables), use the `move` keyword
   |
89 |         let result = LdapQuery::execute(|q| async move {
   |                                                   ++++

Ah, I see. The

        F: FnOnce(&Self) -> Fut,
        Fut: Future<Output = CrateResult<T>>,

is a problem, as the future of such a function usually depends on the lifetime (i.e. calling F with &'a Self returns a different future type than calling it with &'b Self, etc..)

Compare this thread

there's a workaround (first answer in that thread) that doesn't support closures though (due to current limitations of the rust compiler), so it's not useful for your case Edit: actually, your closure does not capture anything, to it is technically an option; still it's probably too annoying to use in production;
or it’s possible to work with (pinned) Box<dyn Future<...>> (second answer in that thread).

In your case, it also seems reasonable to pass the Self by-value, i.e.

        F: FnOnce(Self) -> Fut,
        Fut: Future<Output = CrateResult<T>>,

and

let result = f(Self { ldap: ldap.clone() }).await;

First, let me thank you for taking the time :pray:

Indeed, my closure does not capture anything yet. The idea is then to capture a Vec<String> of usernames.

I had tried what you proposed (FnOnce(Self), by-value) but it still does not compile :

error[E0373]: async block may outlive the current function, but it borrows `q`, which is owned by the current function
  --> src/ldap.rs:89:51
   |
89 |           let result = LdapQuery::execute(|q| async {
   |  ___________________________________________________^
90 | |             let mut emails = Vec::new();
91 | |             emails.push(q.get_email("user_1").await.unwrap());
   | |                         - `q` is borrowed here
92 | |             emails.push(q.get_email("user_2").await.unwrap());
93 | |             Ok(emails)
94 | |         })
   | |_________^ may outlive borrowed value `q`

The thing is I'm not sure it is the right way to use an async block in this situation.

As you can see, I'm still not at ease with Rust harder concepts :joy:

I will look at the second answer in the thread you mentioned now.

As a follow up, dear @steffahn, I managed to get it working, thanks to your directions.

I'm still confused and am not sure the code is any simpler at call site then (as opposed to calling open/close methods) :sweat_smile:

Thank you very much!

1 Like

I suppose all that might be needed here is to change async to async move? (I think in one of the error messages the compiler even suggested so.)

Sorry, I didn’t see/notice this answer earlier as you did reply to your own topic, not to my reply :wink:

Ah snap, sorry mate :scream:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.