The state of Async in Rust

I'm learning Rust at the moment, and I found out that there are so many async in Rust.

This is an async/await from this mongodb-rust Tutorial

self.get_collection()
            .insert_one(doc, None)
            .await
            .map_err(MongoQueryError)?;

This is also an async/await with different set of rule to use and match the error. This one use Result<T, E> enum. This particular example comes from here

 let future = new_example_future();
 let mapped = future.map(|i| i * 3);

 let expected = Ok(6);
 assert_eq!(mapped.wait(), expected);

and there are 2 more external crates like tokio and async-std that provide the same thing basically, with again.. different set of rules. Some crate (like: mongodb driver for Rust) necessitates us to use external crates like tokio or async-std

#[tokio::main] // tokio requires us to use this macro
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;
}

I'm coming from javascript and I'm familiar with async/await in Javascript. But, we do need async await keywords since Javascript is by default, a non-blocking language. If the javascript "engine" finds a rather long to complete' block of code, it will "skip" the block and continue to execute any code after that block, so we need a handler to process any data that will be coming later when that block finishes its job.

I think Rust to be a blocking language, it will wait for certain block to finish before continuing, then why do we need async in Rust and why is it has so many of them ?

The purpose of async/await is to allow you to write certain kinds of networking applications in a much more efficient manner than you would be able to with non-async code. There are many other applications where async/await gives you no advantage and just complicates the code.

Note that the example that calls mapped.wait() is from before async/await was introduced to the language, and you should avoid examples that use pre-async/await patterns. The use of .wait() only happens in these older examples, so you can tell that it is pre-async/await if it uses .wait().

As for the comparison to Javascript, you need to use a library like Tokio in Rust to use async/await because the language does not include a runtime in the standard library. You don't need something like Tokio in Javascript because its built in to the standard library there.

Note also that Tokio does not require you to use that macro. You can also build a Runtime object manually and use its block_on or spawn method to enter an async context.

Thank you for the replies. So.. how many ways are there for Rust to handle async/future/promise?
And what pro and cons of each method ?

I'm confused by this since I'm new and each tutorial introduces me to a new way to handle async.

Are you asking for a comparison between Rust and Javascript, or for a comparison between Tokio and async-std and others? At this point, the majority of people just use Tokio.

just a comparison between Tokio and async-std and others within Rust.
If most people use tokio then I think I can safely ignore the others then

The differences between Tokio and async-std are not that large. Mainly, Tokio has had more man hours put into it, and has more configuration knobs, but they are pretty similar overall. The biggest difference between the two is that a lot of libraries require you to use one or the other. The one with the larger ecosystem is Tokio.

For the cases where the libraries you need is available for both runtimes, your code will be relatively similar in either case.

and what about Future (futures - Rust) ?
I hear that it is an official Rust crate that lives in the rust-lang repository. Does it mean that this is "the official" way to handle async function ?

The futures crate has extra utilities that can be used together with any runtime. So it is useful no matter what runtime you choose. However you still need a runtime.

It has an extremely minimal runtime too (futures::executor::block_on), but you are not going to be able to build anything useful with this runtime.

Also, I like to link new users this blog post, which is a short post about a common mistake many new users make.

2 Likes

Many popular projects require Tokio so if you use one that needs Tokio then you have to use it.

If the project doesn't need Tokio, though, I personally recommend the smol executor, which is very simple, lightweight, and in my opinion very well designed.

Still, Tokio is rather frequently used so if you are more concerned with being accustom to using what a lot of people use then probably go with Tokio.