简体   繁体   English

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

[英]How can I use a channel between an async closure and my main thread in rust?

I am trying to use a channel to communicate between an event handler and the main thread of my program using async rust.我正在尝试使用通道在事件处理程序和我的程序的主线程之间使用异步 rust 进行通信。 The event handler in question is this one from the matrix-rust-sdk .有问题的事件处理程序是来自 matrix-rust-sdk的事件处理程序。

I can see that this exact pattern is used in the code here .我可以看到这里的代码中使用了这个确切的模式。

But when I tried literally the same thing in my own code, it gives me a really strange lifetime error.但是当我在自己的代码中尝试同样的事情时,它给了我一个非常奇怪的终身错误。

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

I tried to make a much simpler example, and the weird lifetime error remains:我试图做一个更简单的例子,但奇怪的生命周期错误仍然存在:

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(())
}

Gives me:给我:

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

So how can I use a channel in an async closure?那么如何在异步闭包中使用通道呢? Why doesn't my code work when the code in the matrix-rust-sdk does?当 matrix-rust-sdk 中的代码可以工作时,为什么我的代码不工作?

I think you meant || async move我想你的意思是|| async move || async move instead of move || async || async move而不是move || async move || async . 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;
}

I think in most cases, |args| async move {}我认为在大多数情况下, |args| async move {} |args| async move {} is what you want to use if you want to create an async closure.如果要创建async闭包,则要使用|args| async move {} But I don't completely understand the differences either.但我也不完全理解这些差异。

For more infos, this might help: What is the difference between `|_|有关更多信息,这可能会有所帮助: `|_| 之间有什么区别? async move {}` and `async move |_| 异步移动 {}` 和 `异步移动 |_| {}` . {}` .


I don't think your minimal example represents your actual problem of your real code, though.不过,我认为您的最小示例并不代表您的真实代码的实际问题。 This is a minimal example that represents the real problem:这是一个代表真正问题的最小示例:

#[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

The real problem here is caused by the fact that room_id contains a reference to dm_room_closure , but dm_room_closure does not get kept alive by the innermost async move context.这里真正的问题是由于room_id包含对dm_room_closure的引用,但dm_room_closure并没有被最里面的async move上下文保持活动状态。

To fix this, make sure that the async move keeps dm_room_closure alive by moving it in as well.要解决此问题,请确保async move也通过移动dm_room_closure来保持活动状态。 In this case, this is as simple as creating the room_id variable inside of the async move :在这种情况下,这就像在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

So I finally fixed the error.所以我终于修复了这个错误。 It turns out that something in Room doesn't implement Copy and therefore it was causing some sort of state sharing despite the Clone s.事实证明, Room中的某些东西没有实现Copy ,因此尽管有Clone ,它还是导致了某种 state 共享。 I fixed it by passing the RoomId as a string .通过将RoomId作为字符串传递来修复它。 Since the lifetime error message is entirely opaque, there was no way to see which moved variable was actually causing the problem.由于生命周期错误消息是完全不透明的,因此无法查看哪个移动变量实际上导致了问题。 Off to file a compiler bug report.关闭以提交编译器错误报告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM