运行 后台工作和 return "cancellation"-关闭

Run background work and return "cancellation"-closure

我对 Rust 很陌生,所以这可能是一个初学者问题:假设我想启动一些在后台运行的异步操作,并通过函数调用停止它。所需的 API 如下所示:

let stop = show_loadbar("loading some data...").await;
// load data...
stop().await;

我的代码:

pub fn show_loadbar(text: &str) -> Box<dyn FnOnce() -> Box<dyn Future<Output=()>>>
{
    let (sender, receiver) = channel::bounded::<()>(0);
    let display = task::spawn(async move {
        while receiver.try_recv().is_err() {
            // show loadbar: xxx.await;
        }
        // cleanup: yyy.await;
    });

    // return a function which stops the load bar
    Box::new(move || {
        Box::new(async {
            sender.send(()).await;
            display.await;
        })
    })
}

我玩了很多(创建结构而不是函数和一些组合),但最后,我总是得到这样的错误:

error[E0373]: async block may outlive the current function, but it borrows `sender`, which is owned by the current function
  --> src/terminal/loading.rs:23:24
   |
23 |           Box::new(async {
   |  ________________________^
24 | |             sender.send(()).await;
   | |             ------ `sender` is borrowed here
25 | |             display.await;
26 | |         })
   | |_________^ may outlive borrowed value `sender`

鉴于所描述的 API,是否有可能在 Rust 中实现这样的功能? 独立于此,Rust 的方法是什么?也许这个接口绝对不是 Rust 应该做的。

非常感谢

您看到的直接错误可以通过将 async 更改为 async move 来修复,以便它通过值而不是通过引用捕获 sender。但是尝试使用您的代码会发现更多问题:

  • 你不能(而且可能不需要)等待 show_loadbar(),因为它本身不是异步的。
  • 钉盒装未来可以期待。
  • 有界 async_std 通道的容量不能为 0(如果给定 0,它会崩溃);
  • 处理 sender.send() 返回的错误,例如打开它。
  • (可选)通过返回 impl FnOnce(...) 而不是 Box<dyn FnOnce(...)> 来摆脱外框。

考虑到这些,代码将如下所示:

pub fn show_loadbar(_text: &str) -> impl FnOnce() -> Pin<Box<dyn Future<Output = ()>>> {
    let (sender, receiver) = channel::bounded::<()>(1);
    let display = task::spawn(async move {
        while receiver.try_recv().is_err() {
            // show loadbar: xxx.await;
        }
        // cleanup: yyy.await;
    });

    // return a function which stops the load bar
    || {
        Box::pin(async move {
            sender.send(()).await.unwrap();
            display.await;
        })
    }
}

// usage example:
async fn run() {
    let cancel = show_loadbar("xxx");
    task::sleep(Duration::from_secs(1)).await;
    cancel().await;
}

fn main() {
    task::block_on(run());
}