我正在尝试使用一个通道来使用异步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
,因此尽管有Clone
s,它还是导致了某种状态共享。我通过将RoomId
作为字符串传递来修复它。由于生存期错误消息是完全不透明的,因此无法查看到底是哪个移动的变量导致了问题。关闭以提交编译器错误报告。