You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In {{ 01-async-await pr }}, the miniserve library was refactored to use Rust's async feature. Unfortunately this broke the build, because the new API is not backwards-compatible. Once you merge {{ 01-async-await pr }}, your task is to port the server crate so it has the same functionality as before, but such that it works with the new miniserve API.
Background
Remember — try solving the problem before reading the background. What compilers errors do you get? Do they give any helpful advice? How far can you get before you get stuck? Don't forget to pull the merged PR before you begin.
Futures
A future is a computation that eventually returns a value. Rust represents futures as types that implement the std::future::Future trait. The Future trait has an associated type Output which indicates the kind of value returned by the future. For example, if a function returns impl Future<Output = i32>, then that means "this function returns a future which eventually outputs an i32." If you need a refresher on impl Trait syntax, check out the Rust book chapter on traits.
async keyword
The easiest way to make a future is to use the async keyword. The async keyword can annotate a function:
The Future trait commonly occurs as a trait bound in library functions that take futures as input, or return futures as outputs. For example:
fni_want_a_future<F:Future>(_f:F){}fni_want_an_async_fn<A:Future,B:Fn() -> A>(_f:B){}fnmain(){let fut = async{println!("Hello world");};i_want_a_future(fut);asyncfnfut_fn(){println!("Hello world");}i_want_an_async_fn(fut_fn);}
If you get a compiler error that says Foo is not a future, that means that Foo needs to implement the Future trait.
await keyword
The easiest way to get the value out of a future is to use the await keyword. For example, you could await the output of returns_a_future like this:
asyncfnprint_zero(){let zero = returns_a_future().await;println!("{zero}");}
The await keyword can only be used in async functions or blocks. It basically means: "wait until this future has completed, and then get its value."
Async in main
Rust does not allow the main function to be async. The reason has to do with how Rust futures are implemented, which we will discuss later. For now, the key thing to know is that async functions need to be executed in a runtime. An async runtime is code that runs futures and checks them for completion. Unlike most languages with async (e.g., NodeJS, C#, Go, etc.), Rust does not have a default async runtime. You have to provide one yourself.
Providing an async runtime
For this tutorial, we will be using Tokio, a popular and featureful async runtime (but the core concepts will translate to other runtimes). miniserve is already using Tokio. To use it within server, add it as a dependency using the full feature, e.g.,
cargo add tokio --features full -p server
The simplest way to setup the Tokio runtime is to annotate the main function like this:
#[tokio::main]asyncfnmain(){// ...}
How does #[tokio::main] work?
The macro isn't too complicated — try seeing what it expands to by running cargo expand. Basically, it moves the body of the main function into an async block, and then defines a new (non-async) main which passes that async block to a newly-created Tokio runtime.
The text was updated successfully, but these errors were encountered:
Task
In {{ 01-async-await pr }}, the
miniserve
library was refactored to use Rust's async feature. Unfortunately this broke the build, because the new API is not backwards-compatible. Once you merge {{ 01-async-await pr }}, your task is to port theserver
crate so it has the same functionality as before, but such that it works with the new miniserve API.Background
Remember — try solving the problem before reading the background. What compilers errors do you get? Do they give any helpful advice? How far can you get before you get stuck? Don't forget to pull the merged PR before you begin.
Futures
A future is a computation that eventually returns a value. Rust represents futures as types that implement the
std::future::Future
trait. TheFuture
trait has an associated typeOutput
which indicates the kind of value returned by the future. For example, if a function returnsimpl Future<Output = i32>
, then that means "this function returns a future which eventually outputs ani32
." If you need a refresher onimpl Trait
syntax, check out the Rust book chapter on traits.async
keywordThe easiest way to make a future is to use the
async
keyword. Theasync
keyword can annotate a function:Or it can annotate block:
These two pieces of code do the same thing.
Future bounds
The
Future
trait commonly occurs as a trait bound in library functions that take futures as input, or return futures as outputs. For example:If you get a compiler error that says
Foo is not a future
, that means thatFoo
needs to implement theFuture
trait.await
keywordThe easiest way to get the value out of a future is to use the
await
keyword. For example, you could await the output ofreturns_a_future
like this:The
await
keyword can only be used inasync
functions or blocks. It basically means: "wait until this future has completed, and then get its value."Async in
main
Rust does not allow the
main
function to beasync
. The reason has to do with how Rust futures are implemented, which we will discuss later. For now, the key thing to know is that async functions need to be executed in a runtime. An async runtime is code that runs futures and checks them for completion. Unlike most languages with async (e.g., NodeJS, C#, Go, etc.), Rust does not have a default async runtime. You have to provide one yourself.Providing an async runtime
For this tutorial, we will be using Tokio, a popular and featureful async runtime (but the core concepts will translate to other runtimes).
miniserve
is already using Tokio. To use it withinserver
, add it as a dependency using thefull
feature, e.g.,cargo add tokio --features full -p server
The simplest way to setup the Tokio runtime is to annotate the
main
function like this:How does
#[tokio::main]
work?The macro isn't too complicated — try seeing what it expands to by running
cargo expand
. Basically, it moves the body of the main function into an async block, and then defines a new (non-async) main which passes that async block to a newly-created Tokio runtime.The text was updated successfully, but these errors were encountered: