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
我不想保持原样;我希望它的生命周期和结构一样。
- 是解决问题的正常方法吗?
- 如何更正代码?
不幸的是,编译器没有明确说明约束的来源。存储没有任何生命周期注释的 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
任务会执行并保留该值,然后任何其他 await
s 将简单地获取该值。而如果任务returns出错,下一个await
会重试。
我试图从服务器获取 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
我不想保持原样;我希望它的生命周期和结构一样。
- 是解决问题的正常方法吗?
- 如何更正代码?
不幸的是,编译器没有明确说明约束的来源。存储没有任何生命周期注释的 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
任务会执行并保留该值,然后任何其他 await
s 将简单地获取该值。而如果任务returns出错,下一个await
会重试。