Async/await: still don't understand it

Hi, I still struggle to use async/await in context of an actual application.

I have a program that runs an event loop. In this loop I want to do some Io without blocking the loop. Once the Io is done some code should be executed in the loop depending on the Io result.

So far, so good.

But how does this look in Rusts async/await?

It's a little difficult to understand the scope of your question. The very specific answer is to use .await:

async fn foo(reader: MyReader) {
   let mut buf = [0u8; 1024];
   let n = reader.read(&buf).await.unwrap();
   // .. do something with buf[..n] ..
}

But I suspect that this may create more questions than it answer. My favorite resource for learning async these days is the tokio tutorial -- I highly recommend you read through it if you haven't already.

Or are you asking about how to implement custom I/O (i.e. implementing your own Future?).

I know the questions is a bit sparse. My first goal is to send serial commands over the com port and wait for an answer (or timeout) without blocking the main loop.

I assume I have to use tokio-serial?

That does indeed look like an appropriate candidate -- it even comes with a nice example. If you need to do raw binary byte-for-byte reads/writes then the example may be a little excessive. But if you're looking to work with line-based protocols that's a really good starting point.

Your defining your solution. With a problem we are more likely to be able to answer if async is appropriate. You typically don't make your own event loop and use one of the crates that you spawn Futures to.

More than likely if you have your own event loop you will spawn a thread (passing it a sender) to do the io and at the start of the loop try check the channels receiver for the result.

You are right. Maybe it would be better to start with a GUI and use their event loop

The async/await syntax hides the fact that there is a loop. The compiler transforms the code that allows it to return to looping the event loop implicitly on every .await point.

You will need an "executor" that will drive the event loop and poll Future objects when its their turn to run. In Rust tokio is popular, but there are executors for different event loops. For example there's one for GTK.

1 Like

Note that this how all of the popular executors work, but it's not strictly required. It's entirely possible to write one that requires the host (non-async) program to call a function whenever there's idle time that can be used to do work.

As a concrete example, I wrote an executor at one point designed to manage animations in an SDL program. It ran on the graphics thread and everything ran to a standstill once per frame; the next time it was invoked, a frame-specific future became Ready which allowed async work to continue.

3 Likes

Thanks for your help. I think I will start to learn Iced, than port this to Tokio and add my IO stuff.