Reputation: 2590
I'm quite new to Rust, so this might be quite a beginner question: Assume I want to start some async action that runs in the background and with a function call I want to stop it. The desired API looks like this:
let stop = show_loadbar("loading some data...").await;
// load data...
stop().await;
My code:
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;
})
})
}
I played around quite a lot (creating a struct instead of a function and some combinations), but finally, I always get error like this:
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`
Given the described API, is it even possible to implement the function like this in Rust? Independent of this, what is the Rust-way to do it? Maybe this interface is absolutely not how it should be done in Rust.
Thank you very much
Upvotes: 1
Views: 397
Reputation: 154906
The immediate error you see can be fixed by changing async
to async move
, so that it captures sender
by value instead of by reference. But trying tu use your code reveals futher issues:
show_loadbar()
, since it's not itself async.async_std
channel cannot have the capacity of 0 (it panics if given 0);sender.send()
, e.g. by unwrapping it.impl FnOnce(...)
instead of Box<dyn FnOnce(...)>
.With these taken into account, the code would look like this:
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());
}
Upvotes: 2