如何传递包含引用参数的异步回调?

How do I pass an async callback containing an argument that is a reference?

我有一个函数需要异步回调(请求处理程序);我目前正在尝试接受看起来像这样的东西:

async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse

它一直在工作,直到添加第二个参数 body,这让我很伤心。接受参数的函数如下所示:

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>,
{

通过这个函数的一部分,我们调用了一个辅助函数:

handle_request(&mut connection, request_handler, request)

具有非常相似的签名;特别是,request_handler 的签名是相同的。它在调用 request_handler 之前进行了一些小的预处理。当我尝试编译它时,我得到:

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)
    |                     ^^^^^^^^^^^^^^

为什么我需要这个/我该怎么办?确实在 where 中将 'static 添加到 H: 似乎可以消除错误,但这是正确的做法吗?实现 H 的类型不能携带引用,而 'static 会禁止这样做吗?我不一定想这样做,但是尝试将 不是 'static 的一生注释到 H 上的任何尝试都没有用;例如,'a + Fn(...) -> F + 'a 不起作用,添加新的通用 'b 生命周期也不起作用。我不想 'static 不需要它的东西,但我不知道该怎么做。

(我对消息的措辞也有点困惑——参数类型——不是一些参数或变量——没有足够长的时间。如何一种活得不够长的类型?)

我尝试了更多的东西,但我仍然无法得到任何实际编译的东西。 This playground example 显示了我 运行 遇到的另一个更令人费解的错误消息。我试过删除一些生命周期注释,并移动了 for<'a> 位(我不确定有什么区别?)。

当回调用 async 关键字标记时,作为引用传递的回调参数不适用于 HRTB 约束。

使用async/await的签名:

async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse

相当于:

fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> Future<Output=HandlerResponse> + 'a

这意味着异步函数的输入生命周期在 异步函数返回的未来。

参见 RFC 2394 的第 "Lifetime capture in the anonymous future" 段。

声明接受参数的函数为:

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>,
{

给出编译错误,因为HRTB要求:

for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a

"unlink" 从调用者绑定的生命周期并产生编译错误 expected bound lifetime parameter 'a, found concrete lifetime

有关 HRTB 的更多详细信息,请参阅

要让它工作,你必须写:

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>,
{

但这会让你遇到另一个问题:

`body` does not live long enough

因为本地主体结构不会比 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!();
}

如果可行 一种可能的解决方案是使用 Boxed 特征对象并摆脱 HTRB 约束。