Rust 在等待同一个未来时需要静态生命周期吗?

Rust needs static lifetime when waiting on the same future?

我试图从服务器获取 OAuth 令牌,所以我创建了一个名为 Token 的结构。为了在令牌过期时只保留一个请求,我保留了一个请求的参考,让其他请求继续等待它returns。 Playground:

use futures::lock::Mutex; // 0.3.18
use futures_util::{future::Shared, Future, FutureExt}; // 0.3.18
use std::{pin::Pin, sync::Arc};
use tokio::time::Duration; // 1.14.0

pub struct Token {
    pub token: String,
    pub requesting:
        Arc<Mutex<Option<Shared<Pin<Box<dyn Future<Output = Result<String, ()>> + Send>>>>>>,
}

impl Token {
    // Get token from server, I hope it does not run repeatly
    async fn get_access_token_from_server(&mut self) -> Result<String, ()> {
        tokio::time::sleep(Duration::from_secs(10)).await;
        self.token = "test".to_owned();
        Ok(self.token.clone())
    }

    //Shows error:`is required to live as long as `'static` here`, why?
    pub async fn access_token(&mut self) -> Result<String, ()> {
        /*
        if !self.token.is_expire() {
            return Ok(self.token.clone());
        }
        */

        let req_arc = self.requesting.clone();
        let req_mutex = req_arc.lock().await;

        let req = if req_mutex.is_some() {
            req_mutex.clone().map(|s| s.clone()).unwrap()
        } else {
            let req = self.get_access_token_from_server().boxed().shared();
            *req_mutex = Some(req.clone());
            req
        };

        req.await
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    /*
        Just example, in the real code, the token will keep in another struct with long life
    */
    let mut token = Token {
        token: String::from(""),
        requesting: Arc::new(Mutex::new(None)),
    };
    let token_string = token.access_token().await;
    println!("{:?}", token_string);
    Ok(())
}

我对生命周期错误感到困惑:

error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:21:31
   |
21 |     pub async fn access_token(&mut self) -> Result<String, ()> {
   |                               ^^^^^^^^^
   |                               |
   |                               this data with an anonymous lifetime `'_`...
   |                               ...is captured here...
...
35 |             *req_mutex = Some(req.clone());
   |                               ----------- ...and is required to live as long as `'static` here

我不想保持原样;我希望它的生命周期和结构一样。

  1. 是解决问题的正常方法吗?
  2. 如何更正代码?

不幸的是,编译器没有明确说明约束的来源。存储没有任何生命周期注释的 dyn Future<...> 默认为 'static 并且你不能真正将其更改为 self 的生命周期,因为自引用结构是 .

考虑到您的目标,您可以使用线程安全且 async 可用的 OnceCell(例如来自 tokio)并跳过所有这些复杂性。

use tokio::time::Duration;
use tokio::sync::OnceCell;

pub struct Token {
    pub token: OnceCell<String>,
}

impl Token {
    pub async fn access_token(&self) -> Result<&String, ()> {
        self.token.get_or_try_init(|| async {
            // simulate auth call
            tokio::time::sleep(Duration::from_secs(1)).await;
            Ok("test".to_owned())
        }).await
    }
}

查看它在 playground 上的工作情况。

我认为它应该像您期望的那样运行。多个线程可以 await 它,但只有一个 async 任务会执行并保留该值,然后任何其他 awaits 将简单地获取该值。而如果任务returns出错,下一个await会重试。