如何在rust中使用异步闭包和主线程之间的通道



我正在尝试使用一个通道来使用异步rust在事件处理程序和程序的主线程之间进行通信。有问题的事件处理程序是矩阵rust-sdk中的一个。

我可以看到这里的代码中使用了这个确切的模式。

但是,当我在自己的代码中尝试同样的东西时,它给了我一个非常奇怪的终身错误。

error: lifetime may not live long enough
--> src/main.rs:84:13
|
80 |           move |event: OriginalSyncRoomMessageEvent, room: Room| {
|           ------------------------------------------------------
|           |                                                    |
|           |                                                    return type of closure `impl futures_util::Future<Output = ()>` contains a lifetime `'2`
|           lifetime `'1` represents this closure's body
...
84 | /             async move {
85 | |                 if let Room::Joined(room) = room {
86 | |                     if room.room_id() == room_id {
87 | |                         match event.content.msgtype {
...  |
94 | |                 }
95 | |             }
| |_____________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

我试着做一个更简单的例子,但奇怪的终身错误仍然存在:

use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> anyhow::Result<()>  {
let (tx, _rx) = mpsc::channel(32);
let closure = move || async  {
at.send("hi");
};
Ok(())
}

给我:

error: lifetime may not live long enough
--> src/main.rs:9:27
|
9  |       let closure = move || async  {
|  ___________________-------_^
| |                   |     |
| |                   |     return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| |                   lifetime `'1` represents this closure's body
10 | |                     at.send("hi");
11 | |     };
| |_____^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

那么,如何在异步闭包中使用通道呢?为什么我的代码不能工作,而矩阵中的代码rust-sdk可以?

我想你指的是|| async move而不是move || async

use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, _rx) = mpsc::channel(32);
let closure = || async move {
tx.send("hi").await.unwrap();
};
closure().await;
}

我认为在大多数情况下,如果您想要创建async闭包,那么|args| async move {}就是您想要使用的。但我也不完全理解其中的区别。

对于更多信息,这可能会有所帮助:"|_|async move{}"one_answers"async move|_|{}"之间有什么区别。


不过,我不认为您的最小示例代表了真实代码的实际问题。这是一个代表真正问题的最小例子:

#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
let room_id = dm_room_closure.room_id();
async move {
println!("{}", room_id.0);
}
};
closure().await;
}
error: lifetime may not live long enough
--> src/main.rs:23:9
|
20 |       let closure = move || {
|                     -------
|                     |     |
|                     |     return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
|                     lifetime `'1` represents this closure's body
...
23 | /         async move {
24 | |             println!("{}", room_id.0);
25 | |         }
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

这里真正的问题是由以下事实引起的:room_id包含对dm_room_closure的引用,但dm_room_closure不能通过最内部的async move上下文保持活动。

要解决此问题,请确保async move也通过将其移入来保持dm_room_closure的活动状态。在这种情况下,这就像在async move:的内部创建room_id变量一样简单

#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
async move {
let room_id = dm_room_closure.room_id();
println!("{}", room_id.0);
}
};
closure().await;
}
42

所以我终于修复了这个错误。事实证明,Room中的某些东西没有实现Copy,因此尽管有Clones,它还是导致了某种状态共享。我通过将RoomId作为字符串传递来修复它。由于生存期错误消息是完全不透明的,因此无法查看到底是哪个移动的变量导致了问题。关闭以提交编译器错误报告。

最新更新