Reputation: 44256
I have a function that requires an asynchronous callback (a request handler); I'm currently trying to accept things that look like this:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
It had been working up until the addition of the second parameter body
, which is causing me grief. The function that accepts the parameter looks like this:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Part way through this function, we call a helper function:
handle_request(&mut connection, request_handler, request)
which has a very similar signature; in particular, the signature for request_handler
is identical. It does some minor pre-processing before invoking request_handler
. When I attempt to compile this, I get:
error[E0310]: the parameter type `H` may not live long enough
|
106 | pub async fn process_requests<H, F>(
| - help: consider adding an explicit lifetime bound `H: 'static`...
...
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
|
note: ...so that the type `H` will meet its required lifetime bounds
|
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
Why do I need this / what do I do about this? Indeed adding 'static
to H:
in the where
does seem to silence the error, but is that the right thing to do? Couldn't the type implementing H
carry a reference, and 'static
would forbid that? I don't necessarily want to do that, but any amount of trying to annotate a lifetime that isn't 'static
onto H
has not worked; e.g., 'a + Fn(...) -> F + 'a
does not work, nor does adding a new generic 'b
lifetime. I'd rather not 'static
something that doesn't need it, but I don't see how to do that.
(I'm also a bit perplexed by the wording of the message — that the parameter type — not some argument or variable — doesn't live long enough. How does a type not live long enough?)
I've played with things a bit more, but I still can't get anything that actually compiles. This playground example shows another one of the more perplexing error messages that I'm running into. I've tried dropping some of the lifetime annotations, and moved the for<'a>
bit (I'm not sure what the difference is?).
Upvotes: 4
Views: 2675
Reputation: 18923
A callback argument passed as reference does not work with HRTB constraints when the callback is marked with the async
keyword.
The signature using async/await
:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
Is equivalent to:
fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> Future<Output=HandlerResponse> + 'a
This implies that input lifetimes of an async function are captured in the future returned by the async function.
See the paragraph "Lifetime capture in the anonymous future" of RFC 2394.
Declaring the function that accepts the parameter as:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Give a compilation error because the HRTB requirement:
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a
"unlink" the lifetime bound from the caller and produce the compilation error
expected bound lifetime parameter 'a, found concrete lifetime
for more details about HRTB see here.
To make it works you have to write:
pub async fn process_requests<'a, H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
But this get you to another problem:
`body` does not live long enough
because the local body struct does not outlive request_handler
:
async fn handle_request<'a, H, F>(
request_handler: &H,
request: Request,
) -> io::Result<()>
where
H: Fn(Request, &'a mut (dyn AsyncRead + 'a)) -> F,
F: Future<Output = String>,
{
let mut body = Body {};
request_handler(request, &mut body);
unimplemented!();
}
If feasible one possible solution could be to use Boxed trait objects and get rid off HTRB constraints.
Upvotes: 2