Rust Async Borrow Lifetimes - asynchronous

I'm trying to make a helper that allows asynchronously chaining side effects, but I'm unable to get the generic bounds correct so that the compiler understands the output of the future outlives a reference used to build it.
Playground Link
The gist of it comes down to:
struct Chain<T> {
data: T
}
impl<T> Chain<T> {
pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
where
Fut: Future<Output=Result<(), E>>,
F: FnOnce(&T) -> Fut
{
todo!()
}
}
gives a compiler error of
error: lifetime may not live long enough
--> src/main.rs:39:32
|
39 | let r = chain.chain(|this| this.good("bar")).await;
| ----- ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `impl Future` contains a lifetime `'2`
| has type `&'1 MyData`
If we fix up chain so that it can infer that the reference is available for the same lifetime as the future:
impl<T> Chain<T> {
pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
where
T: 'a,
Fut: 'a + Future<Output=Result<(), E>>,
F: FnOnce(&'a T) -> Fut
{
effect(&self.data).await?;
Ok(self.data)
}
}
We get a new compiler error about moving self.data while it's still borrowed.
error[E0505]: cannot move out of `self.data` because it is borrowed
--> src/main.rs:30:12
|
23 | pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
| -- lifetime `'a` defined here
...
29 | effect(&self.data).await?;
| ------------------
| | |
| | borrow of `self.data` occurs here
| argument requires that `self.data` is borrowed for `'a`
30 | Ok(self.data)
| ^^^^^^^^^ move out of `self.data` occurs here
I guess there's a pathological closure along the lines of |this| futures::future::ready(Err(this)) that would cause an early return with the borrow still "alive".
Question
How can we get chain to work? My normal lifetime trick of block-scoping doesn't seem to help. Is there a set of where constraints that can be added to prove that the borrow and then eventual move are on disjoint lifetimes?

This particular situation is one where the current constraint syntax and lack of higher-kinded types does not let you express what you want.
You can use a higher-rank trait bound, the for<'a> syntax, to introduce an intermediate generic lifetime parameter 'a within a where clause to dictate that the constraint must be valid for any lifetime. This is necessary here and the reason your first fix didn't work was because 'a as a generic on chain meant that the lifetime was determined by the caller, however, lifetime of self is by construction less than any lifetime that could be picked by the caller. So the slightly more correct syntax (and identical to the de-sugared original code) would be:
pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
where
Fut: Future<Output = Result<(), E>>,
F: for<'a> FnOnce(&'a T) -> Fut
{
...
But this doesn't help at all, since there is still no association between Fut and 'a. There's unfortunately no way to use the same for<'a> across multiple constraints. You could try using impl Trait to define it all at once, but that isn't supported:
pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> + 'a)
{
...
error[E0562]: `impl Trait` not allowed outside of function and method return types
--> src/lib.rs:35:44
|
35 | where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> + 'a)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There will hopefully be better support for higher-kinded types in the future. This particular case might have a solution on nightly by using the almost-complete generic associated types feature, but I haven't yet found it.
So the only real fix then is to use a named type as the return value, which really only leaves us with trait objects:
use std::pin::Pin;
use futures::future::FutureExt;
pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
where F: for<'a> FnOnce(&'a T) -> Pin<Box<dyn Future<Output = Result<(), E>> + 'a>>
{
...
let r = chain.chain(|this| this.good("bar").boxed()).await;
As a side note, your bad case still does not compile and indeed cannot work, since you'd be returning a reference to a local value.

It looks like you are trying to implement future.then()
If you are aware of that and you are doing it as an exercise, you probably should design it in a way that the effect method returns values, and use these values to return from chain method. That way you enforce proper order of operations. As far as I understand your design, you do not benefit from awaiting on effect inside the chain method, since as your skip function is also async and will return future (the actual return type of chain method is Future<Output=Result<T, E>>, since async works that way: it wraps your explicit return type in future).
So there is no point in awaiting on the effect inside the chain, you still have to await it whenever you use it - and nothing will happen until you actually await for it outside of the chain - futures are lazy that way.
TL;DR I would arange your effect methods to return values and arrange chain to just return these values

Related

How to store a pointer to an async method in a container?

I have a struct that defines multiple async methods, and I'd like to store a pointer of each of them in a HashMap, so that I can call any method in one single line, knowing only a key that is given in parameter.
The aim here is to avoid as much as possible to have a huge match clause that would inflate more and more as I add new methods to my struct.
Methods all have the same signature:
async fn handle_xxx(&self, c: Command) -> Result<String, ()>
And I'd really like to call them the following way:
pub async fn execute_command(&mut self, command: Command) -> Result<String, ()> {
let handler = self.command_callbacks.get(&command);
let return_message: String = match handler {
Some(f) => f(self, command).await.unwrap(), // The call is made here.
None => return Err(()),
};
Ok(return_message)
}
However, obviously, in order to store something in a HashMap, you have to specify its type when declaring the HashMap, and that's when the trouble starts.
I tried the most obvious, which is declaring the wrapping function type:
type CommandExecutionNotWorking = fn(&CommandExecutor, Command) -> Future<Output = Result<String, ()>>;
Which does not work since Future is a trait, and not a type.
I tried to declare a generic type and specify it somewhere below:
type CommandExecutionNotWorkingEither<Fut> = fn(&CommandExecutor, Command) -> Fut;
But I encounter the same kind of issue since I need to specify the Future type, and have a HashMap declaration like following:
let mut command_callbacks: HashMap<
Command,
CommandExecutionFn<dyn Future<Output = Result<String, ()>>>,
> = HashMap::new();
impl Future obviously does not work since we're not in a function signature, Future either since it's not a type, and dyn Future creates a legitimate type mismatch.
Thus I tried to use Pin so that I can manipulate dyn Futures, and I ended up with the following signature:
type CommandExecutionStillNotWorking = fn(
&CommandExecutor,
Command,
) -> Pin<Box<dyn Future<Output = Result<String, ()>>>>;
But I need to manipulate functions that return Pin<Box<dyn Future<...>>> and not just Futures. So I tried to define a lambda that take an async function in parameter and returns a function that wraps the return value of my async method in a Pin<Box<...>>:
let wrap = |f| {
|executor, command| Box::pin(f(&executor, command))
};
But the compiler is not happy since it expects me to define the type of f, which is what I tried to avoid here, so I'm back to square one.
Thus my question: Do you know if it's actually possible to write the type of an async function so that pointers on them can be easily manipulated like any variable or any other function pointer?
Or should I go for another solution that may be less elegant, with a bit of duplication code or a huge match structure?
TL;DR: Yes, it is possible, but probably more complicated than you imagined.
First, closures cannot be generic, and thus you'd need a function:
fn wrap<Fut>(f: fn(&CommandExecutor, Command) -> Fut) -> CommandExecution
where
Fut: Future<Output = Result<String, ()>>
{
move |executor, command| Box::pin(f(executor, command))
}
But then you cannot turn the returned closure into a function pointer because it captures f.
Now technically it should be possible since we only want to work with function items (that are non-capturing), and their type (unless converted into a function pointer) is zero-sized. So by the type alone we should be able to construct an instance. But doing that requires unsafe code:
fn wrap<Fut, F>(_f: F) -> CommandExecution
where
Fut: Future<Output = Result<String, ()>>,
F: Fn(&CommandExecutor, Command) -> Fut,
{
assert_eq!(std::mem::size_of::<F>(), 0, "expected a fn item");
move |executor, command| {
// SAFETY: `F` is a ZST (checked above), any (aligned!) pointer, even crafted
// out of the thin air, is valid for it.
let f: &F = unsafe { std::ptr::NonNull::dangling().as_ref() };
Box::pin(f(executor, command))
}
}
(We need _f as a parameter because we cannot specify the function type; let inference find it out itself.)
However, are troubles don't end here. They just start.
Now we get the following error:
error[E0310]: the parameter type `Fut` may not live long enough
--> src/lib.rs:28:9
|
18 | fn wrap<Fut, F>(_f: F) -> CommandExecution
| --- help: consider adding an explicit lifetime bound...: `Fut: 'static`
...
28 | Box::pin(f(executor, command))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `Fut` will meet its required lifetime bounds
Well, it suggests a solution. Let's try...
It compiles! Successfully!!
...until we actually try to make use of it:
let mut _command_callbacks: Vec<CommandExecution> = vec![
wrap(CommandExecutor::handle_xxx),
wrap(CommandExecutor::handle_xxx2),
];
(a HashMap will have the same effect).
error[E0308]: mismatched types
--> src/lib.rs:34:9
|
34 | wrap(CommandExecutor::handle_xxx),
| ^^^^ lifetime mismatch
|
= note: expected associated type `<for<'_> fn(&CommandExecutor, Command) -> impl Future<Output = Result<String, ()>> {CommandExecutor::handle_xxx} as FnOnce<(&CommandExecutor, Command)>>::Output`
found associated type `<for<'_> fn(&CommandExecutor, Command) -> impl Future<Output = Result<String, ()>> {CommandExecutor::handle_xxx} as FnOnce<(&CommandExecutor, Command)>>::Output`
= note: the required lifetime does not necessarily outlive the static lifetime
note: the lifetime requirement is introduced here
--> src/lib.rs:21:41
|
21 | F: Fn(&CommandExecutor, Command) -> Fut,
| ^^^
error[E0308]: mismatched types
--> src/lib.rs:35:9
|
35 | wrap(CommandExecutor::handle_xxx2),
| ^^^^ lifetime mismatch
|
= note: expected associated type `<for<'_> fn(&CommandExecutor, Command) -> impl Future<Output = Result<String, ()>> {CommandExecutor::handle_xxx2} as FnOnce<(&CommandExecutor, Command)>>::Output`
found associated type `<for<'_> fn(&CommandExecutor, Command) -> impl Future<Output = Result<String, ()>> {CommandExecutor::handle_xxx2} as FnOnce<(&CommandExecutor, Command)>>::Output`
= note: the required lifetime does not necessarily outlive the static lifetime
note: the lifetime requirement is introduced here
--> src/lib.rs:21:41
|
21 | F: Fn(&CommandExecutor, Command) -> Fut,
| ^^^
The problem is described in Lifetime of a reference passed to async callback. The solution is to use a trait to workaround the problem:
type CommandExecution = for<'a> fn(
&'a CommandExecutor,
Command,
) -> Pin<Box<dyn Future<Output = Result<String, ()>> + 'a>>;
trait CommandExecutionAsyncFn<CommandExecutor>:
Fn(CommandExecutor, Command) -> <Self as CommandExecutionAsyncFn<CommandExecutor>>::Fut
{
type Fut: Future<Output = Result<String, ()>>;
}
impl<CommandExecutor, F, Fut> CommandExecutionAsyncFn<CommandExecutor> for F
where
F: Fn(CommandExecutor, Command) -> Fut,
Fut: Future<Output = Result<String, ()>>,
{
type Fut = Fut;
}
fn wrap<F>(_f: F) -> CommandExecution
where
F: 'static + for<'a> CommandExecutionAsyncFn<&'a CommandExecutor>,
{
assert_eq!(std::mem::size_of::<F>(), 0, "expected a fn item");
move |executor, command| {
// SAFETY: `F` is a ZST (checked above), any (aligned!) pointer, even crafted
// out of the thin air, is valid for it.
let f: &F = unsafe { std::ptr::NonNull::dangling().as_ref() };
Box::pin(f(executor, command))
}
}
I will not expand on why this thing is needed or how it solves the problem. You can find an explanation in the linked question and the questions linked in it.
And now our code works. Like, truly so.
However, think carefully if you really want all of this stuff: it may be easier to just change the functions to return a boxed future.

How to use an async closure both capturing and accepting references

I am trying to pass a closure returning Future<Output=bool> to an async function and call this closure as an async predicate (something like an async .filter or other higher-order function).
This predicate receives its input as a reference. I found how to implement it for pure predicates that do not capture their environment:
type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;
////////////////////////////////////////////////////////////////////////////////
// 1 -> Closure only uses the inner argument //
////////////////////////////////////////////////////////////////////////////////
/// Run the computation and check that its output is not empty
fn run1<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
async move {
let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
b.len() > 0 // Only uses the inner argument
}));
let is_ok1 = is_ok.await;
dbg!(is_ok1);
}
}
/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check1<F>(check: F) -> bool
where
F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r, bool>
{
let bytes = [0u8; 128];
let is_ok = check(&bytes).await;
drop(bytes);
is_ok
}
Playground link
Supporting only non-capturing closures is quite limiting. I would like to use a closure that captures its environment. By changing the bounds of my compute_and_check function, I am able to pass a closure that uses its environment - but not its input:
type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;
////////////////////////////////////////////////////////////////////////////////
// 2 -> Closure only uses the outer argument //
////////////////////////////////////////////////////////////////////////////////
/// Run the computation and assume that its output is not empty if `expected` is not empty
fn run2<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
async move {
let is_ok = compute_and_check2(|b: &[u8]| Box::pin(async move {
expected.len() > 0 // Only uses the environment
}));
let is_ok2 = is_ok.await;
dbg!(is_ok2);
}
}
/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check2<'a, F>(check: F) -> bool
where
F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'a, bool>
{
let bytes = [0u8; 128];
let is_ok = check(&bytes).await;
drop(bytes);
is_ok
}
Playground link
I can write an implementation where the closure uses its input, and one where the closure uses its environment. But not both at the same time.
How can I accept a Future-producing closure that uses references for both its input and environment?
What I would like to write is something like this:
type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;
////////////////////////////////////////////////////////////////////////////////
// 3 -> Closure uses both the inner and outer arguments //
////////////////////////////////////////////////////////////////////////////////
/// Run the computation and check its output is the provided expected value
fn run3<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
async move {
let is_ok = compute_and_check3(|b: &[u8]| Box::pin(async move {
b == expected // Uses both the input and environment
}));
let is_ok2 = is_ok.await;
dbg!(is_ok2);
}
}
/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check3<'a, F>(check: F) -> bool
where
F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r + 'a, bool>
{
let bytes = [0u8; 128];
let is_ok = check(&bytes).await;
drop(bytes);
is_ok
}
Playground link
This code does not compile because I am asking the closure to return BoxFuture<'r + 'a, bool> but this is not legal syntax:
error[E0226]: only a single explicit lifetime bound is permitted
--> src/main.rs:89:51
|
89 | F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r + 'a, bool>
| ^^
From what I understand, the core of my issue is that I need to restrict my Higher-Ranked Trait Bound. Instead of "for any 'r", I want to say "for any 'r that does not outlive 'a" but I don't see how to write it down.
I tried to use two lifetimes and restrictions to my BoxFuture type alias, or define an auxilliary trait but I did not manage to solve this issue because I fail to apply restrictions on the HRTB lifetime.
For completness, here are the errors I get when passing my final closure to compute_and_check1 (only input) and compute_and_check2 (only environment):
Using compute_and_check1 (only input) playground link
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:17:67
|
17 | let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
| ___________________________________________________________________^
18 | | b == expected
19 | | }));
| |_____^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 15:9...
--> src/main.rs:15:9
|
15 | fn run3<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
| ^^
note: ...so that the types are compatible
--> src/main.rs:17:67
|
17 | let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
| ___________________________________________________________________^
18 | | b == expected
19 | | }));
| |_____^
= note: expected `(&[u8], &[u8])`
found `(&[u8], &'a [u8])`
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the body at 17:36...
--> src/main.rs:17:36
|
17 | let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
| ____________________________________^
18 | | b == expected
19 | | }));
| |______^
note: ...so that the expression is assignable
--> src/main.rs:17:47
|
17 | let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
| _______________________________________________^
18 | | b == expected
19 | | }));
| |______^
= note: expected `Pin<Box<dyn Future<Output = bool> + Send>>`
found `Pin<Box<dyn Future<Output = bool> + Send>>`
Using compute_and_check2 (only environment) playground link
error: lifetime may not live long enough
--> src/main.rs:17:47
|
17 | let is_ok = compute_and_check2(|b: &[u8]| Box::pin(async move {
| ________________________________________-____-_^
| | | |
| | | return type of closure is Pin<Box<(dyn Future<Output = bool> + Send + '2)>>
| | let's call the lifetime of this reference `'1`
18 | | b == expected
19 | | }));
| |______^ returning this value requires that `'1` must outlive `'2`
I have also looked into the unboxed_closure nightly feature but did not manage to solve my issue. I would prefer my code to work on stable Rust but it is acceptable for my code to require nightly if it is the only solution.

What is the concrete type of a future returned from `async fn`?

What type should I use for a vector that stores futures?
I tried to make multiple concurrent requests on the same URL and save all the futures into the vector to use with join_all.
If I don't set a type for the vector explicitly, everything works. I understand that Rust can find the proper type of a variable. CLion determines the vector type as Vec<dyn Future<Output = ()>>, but when I try to set the type by myself, it gives me an error:
error[E0277]: the size for values of type `dyn core::future::future::Future<Output = ()>` cannot be known at compilation time
--> src/lib.rs:15:23
|
15 | let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn core::future::future::Future<Output = ()>`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required by `std::vec::Vec`
I must declare the type as Vec<Pin<Box<dyn Future<Output=()>>>> which forces me to wrap result of function into requests.push(Pin::from(Box::new(request(i))));
use futures::future::join_all;
use std::future::Future;
use std::pin::Pin;
async fn request(n: i32) {
println!("Started: {}", n);
let response = reqwest::get("https://www.rust-lang.org")
.unwrap()
.text()
.unwrap();
println!("Completed: {}. Response: {}", n, &response[0..10]);
}
async fn main() {
let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
for i in 0..5 {
requests.push(request(i));
}
join_all(requests).await;
}
Which type it should be?
From the RFC:
The return type of an async function is a unique anonymous type generated by the compiler, similar to the type of a closure. You can think of this type as being like an enum, with one variant for every "yield point" of the function - the beginning of it, the await expressions, and every return. Each variant stores the state that is needed to be stored to resume control from that yield point.
When the function is called, this anonymous type is returned in its
initial state, which contains all of the arguments to this function.
You can't explicitly declare the concrete type of a future since it is an anonymous type. As an API user we only need to know that it implements std::futures::Future but this doesn't mean that we don't need a deeper knowledge of this anonymous type and it's implementation, it would be nice to have for grasping the concept.
CLion determines the vector type as Vec<dyn Future<Output = ()>>
This is a type hint, not the actual type, since compiler is not able to know the size of dyn Future<Output = ()>, it will not be compiled.
Pin<Box<_>>-ing a Future to declare an explicit type might not be a good idea. In your case it's not needed because the concrete types returned from async fn are identical. Letting the compiler infer the type will just be fine.
See also:
For various concrete return types :
How can I put an async function into a map in Rust?
Static & Dynamic dispatch : Trait Objects

How to call to_socket_addrs on a collection of addresses?

How do I call to_socket_addrs() on an array or vector in Rust? The following code does not compile:
extern crate ws;
use std::net::ToSocketAddrs;
fn main() {
let addrs = ["127.0.0.1:8889", "127.0.0.1:0"]
.iter()
.flat_map(|a| a.to_socket_addrs());
ws::listen(addrs, |out| move |msg| out.send(msg));
}
error[E0277]: the trait bound `std::iter::FlatMap<std::slice::Iter<'_, &str>, std::result::Result<std::vec::IntoIter<std::net::SocketAddr>, std::io::Error>, [closure#src/main.rs:7:19: 7:42]>: std::net::ToSocketAddrs` is not satisfied
--> src/main.rs:8:5
|
8 | ws::listen(addrs, |out| move |msg| out.send(msg));
| ^^^^^^^^^^ the trait `std::net::ToSocketAddrs` is not implemented for `std::iter::FlatMap<std::slice::Iter<'_, &str>, std::result::Result<std::vec::IntoIter<std::net::SocketAddr>, std::io::Error>, [closure#src/main.rs:7:19: 7:42]>`
|
= note: required by `ws::listen`
It took me two hours to understand the error because "trait X is not implemented for std::iter::FlatMap" reads like the problem is caused by flat_map, instead of the demands of ws::listen.
to_socket_addrs returns a Result containing an iterator. Result itself implements IntoIterator, so flat_map flattens the result, but not the inner iterator, you need to do that in an extra step:
use std::net::ToSocketAddrs;
fn main() {
let addrs = ["127.0.0.1:8889", "127.0.0.1:0"]
.iter()
.flat_map(|a| a.to_socket_addrs())
.flatten()
.collect::<Vec<_>>();
println!("addrs = {:?}!", addrs);
}

Running asynchronous mutable operations with Rust futures

I'm building a service in Rust using tokio-rs and was happy with this tech stack so far. I'm now trying to chain up async operations that includes writes and having a hard time with the borrow checker.
My simplified minimal code sample is this:
extern crate futures; // 0.1.21
use futures::Future;
use std::{cell::RefCell, rc::Rc};
trait RequestProcessor {
fn prepare(&self) -> Box<Future<Item = (), Error = ()>>;
fn process(&mut self, request: String) -> Box<Future<Item = (), Error = ()>>;
}
struct Service {
processor: Rc<RefCell<RequestProcessor>>,
}
impl Service {
fn serve(&mut self, request: String) -> Box<Future<Item = (), Error = ()>> {
let processor_clone = self.processor.clone();
let result_fut = self
.processor
.borrow()
.prepare()
.and_then(move |_| processor_clone.borrow_mut().process(request));
Box::new(result_fut)
}
}
fn main() {}
As a short summary, after an async preparation step I'm trying to run another async operation that writes a field of self. Without mutability this works easily with a plain Rc member, but mutability breaks it, producing the following error:
error[E0597]: `processor_clone` does not live long enough
--> src/main.rs:22:32
|
22 | .and_then(move |_| processor_clone.borrow_mut().process(request));
| ^^^^^^^^^^^^^^^ - `processor_clone` dropped here while still borrowed
| |
| borrowed value does not live long enough
|
= note: values in a scope are dropped in the opposite order they are created
I'd expect this should work, I don't see where the mutable reference is still borrowed. I think process() should release the processor's &mut self after the future is returned, so the compile error shouldn't occur.
Could you please explain the underlying reasons? How should this example be changed to be accepted by the compiler?
Sometimes, it's easier to see lifetime errors if you split values up onto multiple lines. Let's try that with the closure in question:
.and_then(move |_| {
let c = processor_clone;
let mut d = c.borrow_mut();
let e = d.process(request);
e
});
If you compile this... it works. If we try recombining the lines, we can get this to fail:
.and_then(move |_| {
let c = processor_clone;
c.borrow_mut().process(request)
});
And this to work:
.and_then(move |_| {
let c = processor_clone;
return c.borrow_mut().process(request);
});
The only difference is the explicit return and the semicolon. This is very similar to When returning the outcome of consuming a StdinLock, why was the borrow to stdin retained?, so let's try the suggestion of one of the answers to enable non-lexical lifetimes. This allows your original code to compile as well.
TL;DR: It's a weakness in Rust's current borrow checker implementation and will be magically fixed at some point. In the meantime, I suggest writing it as two lines:
.and_then(move |_| {
let mut c = processor_clone.borrow_mut();
c.process(request)
});

Resources