I’m creating a simple app using the Simple Async template, and currently based on the simple counter app. When I hit the D key, the program shows a “Scanning” message, does a potentially slow network operation (detect all ONVIF cameras on the network) and then shows the count of detected devices.
It works, but the UI blocks until the network operation has finished, 1 or 2 second. So I never see the “Scanning” message.
I’m trying to use tokio::spawn
to run the network operation asynchronously, but I’m having a hard time with this code:
pub struct App {
pub running: bool,
pub counter: u8,
/// Show the spinner when the app is doing something
pub show_spinner: bool
}
impl App {
pub fn new() -> Self {
Self::default()
}
pub fn set_counter(&mut self, value: u8) {
self.counter = value;
}
pub async fn discover_devices(&mut self) {
self.toggle_spinner();
tokio::spawn(async move {
match core::discover::discover_onvif_devices().await {
Ok(devices) => {
self.set_counter(devices.len() as u8);
}
Err(_e) => {
// error!("Error discovering devices: {}", e);
self.set_counter(0);
}
}
self.toggle_spinner();
});
}
pub fn toggle_spinner(&mut self) {
self.show_spinner = ! self.show_spinner;
}
}
Rust complains with this error:
error[E0521]: borrowed data escapes outside of method
--> tui/src/app.rs:60:9
|
58 | pub async fn discover_devices(&mut self) {
| ---------
| |
| `self` is a reference that is only valid in the method body
| let's call the lifetime of this reference `'1`
59 | self.toggle_spinner();
60 | / tokio::spawn(async move {
61 | | match core::discover::discover_onvif_devices().await {
62 | | Ok(devices) => {
63 | | self.set_counter(devices.len() as u8);
... |
70 | | self.toggle_spinner();
71 | | });
| | ^
| | |
| |__________`self` escapes the method body here
| argument requires that `'1` must outlive `'static`
I’m still trying to understand how async code works on Rust; but what I understand so far is that there’s a possibility that the App instance might be gone by the time my async code finishes so I must do something about it. What I don’t understand is what? Do I need to add a lock on App.counter
or something like that? What are the options available?