Reputation: 587
I am trying to get the OAuth token from the server, so I made a struct named Token
. In order to keep only one request when the token is expired, I keep a reference for the requesting future, let other requests keep waiting for it 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(())
}
I am confused about the lifetime errors:
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
I do not want to keep is as static; I hope its lifetime is the same as the struct.
Upvotes: 0
Views: 606
Reputation: 60457
Unfortunately, the compiler doesn't make it obvious where the constraint comes from. Storing a dyn Future<...>
without any lifetime annotation defaults to 'static
and you can't really change it to the lifetime of self
because self-referential structs are problematic.
Looking at your goal, you can use a thread-safe and async
-capable OnceCell
(from tokio for example) and skip all this complexity.
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
}
}
See it working on the playground.
I think it should behave as you expect. Multiple threads can await
it but only one async
task will execute and persist the value, and then any other await
s will simply fetch that value. And if the task returns an error, the next await
will retry.
Upvotes: 2