I'm trying to write an async function which takes a closure as an argument. The closure is to return a Future and to take a reference to a value which is local to the function to which it was passed. Here's my first attempt.
async fn access<T, Fut, F>(data: &RwLock<String>, accessor: F) -> T
where
F: FnOnce(&str) -> Fut,
Fut: Future<Output = T>,
{
let guard = data.read().await;
accessor(&guard).await
}
I thought this was fine until I tried to use it:
async fn test_access() {
let data = RwLock::new("sehr problematisch".to_owned());
let data_len = access(&data, |message| async move { message.len() }).await;
}
This has a lifetime error because the argument to the closure needs to live longer than the Future returned by it, but I didn't express this constraint in the signature of access
.
error: lifetime may not live long enough
--> src/lib.rs:15:44
|
15 | let data_len = access(&data, |message| async move { message.len() }).await;
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `[async block@src/lib.rs:15:44: 15:72]` contains a lifetime `'2`
| has type `&'1 str`
error: could not compile `playground` due to previous error
(playground link: Rust Playground)
I thought I could fix this by adding lifetime parameters to access
.
async fn access<'a, 'b, T, Fut, F>(data: &'b RwLock<String>, accessor: F) -> T
where
'b: 'a,
F: FnOnce(&'a str) -> Fut,
Fut: 'a + Future<Output = T>,
{
let guard = data.read().await;
accessor(&guard).await
}
However this also failed with a lifetime error, this time in the body of access
.
error[E0597]: `guard` does not live long enough
--> src/lib.rs:11:14
|
4 | async fn access<'a, 'b, T, Fut, F>(data: &'b RwLock<String>, accessor: F) -> T
| -- lifetime `'a` defined here
...
11 | accessor(&guard).await
| ---------^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `guard` is borrowed for `'a`
12 | }
| - `guard` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
(playground link: Rust Playground)
This error makes sense. Because 'a
has no constraints, the borrow checker assumes it lives forever, as the function could be called with 'static'
substituted for this parameter.
What I'd like to do is express to the borrow checker that the value returned from the closure only lives as long as its argument, but without exposing a lifetime parameter to the callers of access
. Are there any language features that allow me to express this?
(edit: fixed syntax highlighting)