See full example - please comment in the commented-out lines to see my issue.
I'm trying to write a actix-web based server to serve views into relatively slow-to-parse data via a JSON API. In order to not re-parse the data on every access, I want to use a shared state to keep a LRU cache of previously-parsed data sets. Let's pretend that the parse_data
function used to generate the data looks like this:
#[derive(Debug)]
struct Datum {
value: String,
}
fn parse_data(id: &str) -> Box<dyn Future<Item = Datum, Error = Error> + Send> {
Box::new(ok(Datum {
value: format!("Data for ID '{}'", id),
}))
}
To cache the "slow" parse_data
function, I want to use the actix-web Data functionality by building up a shared State object that contains an lru_cache::LruCache
to keep already-parsed objects in shared memory:
struct State {
data: Mutex<LruCache<String, Box<dyn Future<Item = Arc<Datum>, Error = Error> + Send>>>,
}
I'm now trying to implement a get
function for State
that will either get a pre-existing future from the LruCache
or create a new future and store that in the cache:
impl State {
fn get(&self, id: &str) -> Box<dyn Future<Item = Arc<Datum>, Error = Error> + Send> {
let locked = self.data.lock().expect("Locking mutex");
if let Some(fut) = locked.get_mut(id) {
println!("Cache hit on ID '{}'", id);
return Box::new(fut);
}
println!("Cache miss on ID '{}", id);
let mut fut_parse = Box::new(parse_data(id).map(Arc::new));
locked.insert(id.to_owned(), fut_parse);
fut_parse
}
}
However, rustc rightly complains about an issue with the lifetimes on the Mutex
lock:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:29:32
|
29 | let locked = self.data.lock().expect("Locking mutex");
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 28:5...
--> src/main.rs:28:5
|
28 | / fn get(&self, id: &str) -> Box<dyn Future<Item = Arc<Datum>, Error = Error> + Send> {
29 | | let locked = self.data.lock().expect("Locking mutex");
30 | |
31 | | if let Some(fut) = locked.get_mut(id) {
... |
42 | | fut_parse
43 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:29:22
|
29 | let locked = self.data.lock().expect("Locking mutex");
| ^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn futures::future::Future<Error = failure::error::Error, Item = std::sync::Arc<Datum>> + std::marker::Send + 'static)>
found std::boxed::Box<dyn futures::future::Future<Error = failure::error::Error, Item = std::sync::Arc<Datum>> + std::marker::Send>
error: aborting due to previous error
error: Could not compile `async-cache-test`.
To learn more, run the command again with --verbose.
I understand that the Future
s returned by the method are affected by the lifetime of locking the Mutex, but I've been struggling with how to change my design to get it working.
Please be aware that in my full example, I've commented out my caching code to show that everything else is working. If you want to reproduce my error message, please remove comments on all commented-out lines or go to the non-commented-out version instead.
Thanks in advance for any hints!