Passing future with lifetime different to static

I have the following simplification of my code:

Code
use futures::future::BoxFuture;
use futures::FutureExt;
use std::future::Future;
use std::sync::Arc;

trait MyTrait: Send + Sync {
    fn is_the_same(&mut self, v: String) -> BoxFuture<'_, bool>;
}

struct HasTrait {
    value: String,
}

impl MyTrait for HasTrait {
    fn is_the_same(&mut self, v: String) -> BoxFuture<'_, bool> {
        async move { self.value == v }.boxed()
    }
}

struct RefToHasTrait {
    has_trait: Arc<async_std::sync::RwLock<dyn MyTrait>>,
}

impl RefToHasTrait {
    pub fn do_on_ref(&mut self, v: String) -> BoxFuture<'_, bool> {
        async move {
            let mut has_trait = self.has_trait.write().await;
            has_trait.is_the_same(v).await
        }
        .boxed()
    }
}

struct Checker {
    ref_to_has_trait: RefToHasTrait,
}

impl Checker {
    pub fn check(&mut self, v: String) -> Option<BoxFuture<'_, bool>> {
        let result = self.ref_to_has_trait.do_on_ref(v);

        Some(Box::pin(result))
    }
}

// I must use this third-party function
pub fn run_future(
    future: impl Future<Output = bool> + 'static + Send,
) -> async_std::task::JoinHandle<bool> {
    async_std::task::spawn(future)
}

fn main() {
    let has_trait = Arc::new(async_std::sync::RwLock::new(HasTrait {
        value: "str".to_string(),
    }));
    let ref_to_has_trait = RefToHasTrait { has_trait };
    let mut checker = Checker { ref_to_has_trait };

    let h = run_future(checker.check("str".to_string()).unwrap());

    async_std::task::block_on(async move {
        h.await;
    });
}

The problem arises because the lifetime of check is smaller than ``'static. Unfortunately after juggling around a bit I am unable to fulfill the lifetime requirement since I must run_future`. I can modify the existing code structure, but I would rather not since that involves a big refactor.

I checked the following past posts (in order of relevance):


But still not sure how to implement the proposed solutions for my structures

Any help is greatly appreciated. If more context is required I'll update my post.

If something wants 'static it means use of temporary references is forbidden. There is no direct solution to this — you can't make it accept anything temporary.

Lifetime of references cannot be extended other than by refactoring code to be structured differently. There's no trick for this, there's no workaround: you must redesign your code to use owned values instead of temporary references.

In your case: you can't use borrowed self inside async move blocks. Use BoxFuture<'static, and compute as much as you can outside the async move block. Instead of using self.value inside async move block, clone the value outside that block, and use the clone inside it.

&mut self means "you can have a peek for a second, until this call ends", which is fundamentally incompatible with spawn's 'static which means "I want to hold on to this indefinitely" (long after self may ceased to exist, or isn't exclusively borrowed any more, so the &mut guarantee is over).

Remember that async code doesn't run when the function is called, but it's run somewhere else, sometime later, after synchronous calls have finished, and all borrows of their arguments are no longer valid. Futures for spawn must be entirely self-contained and independent of the scope they were created in.

3 Likes

That did the trick. Had to use Arc a couple times more. Thank you for the concise explanation.

1 Like

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.