Let’s make the main
function a tokio
entry point.
Add the #[tokio::main]
macro to the main
function and make the function async
. This allows you
to use async
and await
inside main
. You can also now spawn tokio tasks within your
application.
#[tokio :: main]
async fn main () -> color_eyre :: Result<()> {
println! ( " Sleeping for 5 seconds... " );
tokio :: time :: sleep (tokio :: time :: Duration :: from_secs ( 5 )) . await ;
Ok(())
}
You can run this with cargo run
, and you’ll see that the terminal prints and then blocks for 5
seconds before returning control.
$ cargo run
Compiling crates-tui v0.1.0 (~/gitrepos/crates-tui-tutorial)
Finished dev [unoptimized + debuginfo] target ( s ) in 0.31 s
Running ` target/debug/crates-tui-tutorial `
Sleeping for 5 seconds...
$
Tip
On UNIX systems, you can use time cargo run
to see how long a process takes to run.
$ time cargo run
Finished dev [unoptimized + debuginfo] target ( s ) in 0.08 s
Running ` target/debug/crates-tui-tutorial `
Sleeping for 5 seconds...
cargo run 0.09 s user 0.05 s system 2 % cpu 5.262 total
$
In this case, it took 5.262
seconds to run cargo run
.
Homework
Try to predicate what happens if you spawn multiple tokio tasks? e.g.
#[tokio :: main]
async fn main () -> color_eyre :: Result<()> {
println! ( " Spawning a task that sleeps 5 seconds... " );
let mut tasks = vec! [];
for i in 0 .. 10 {
tasks . push (tokio :: spawn ( async move {
println! ( " Sleeping for 5 seconds in a tokio task {i}... " );
tokio :: time :: sleep (tokio :: time :: Duration :: from_secs ( 5 )) . await ;
i
}));
}
println! ( " Getting return values from tasks... " );
while let Some( task ) = tasks . pop () {
let return_value_from_task = task . await ? ;
println! ( " Got i = {return_value_from_task} " );
}
Ok(())
}
Now, what happens if you run the following instead?
#[tokio :: main]
async fn main () -> color_eyre :: Result<()> {
println! ( " Spawning a task that sleeps 5 seconds... " );
let mut tasks = vec! [];
for i in 0 .. 10 {
tasks . push ( async move {
println! ( " Sleeping for 5 seconds in a tokio task {i}... " );
tokio :: time :: sleep (tokio :: time :: Duration :: from_secs ( 5 )) . await ;
i
});
}
println! ( " Getting return values from tasks... " );
while let Some( task ) = tasks . pop () {
let return_value_from_task = task . await ;
println! ( " Got i = {return_value_from_task} " );
}
Ok(())
}
Do you understand the different between creating a future and await
ing on it later versus
spawning a future and await
ing on the spawn’s JoinHandle
later?
Conclusion
We will expand on main.rs
in the following sections. Right now, your project should look like
this:
.
├── Cargo.lock
├── Cargo.toml
└── src
└── main.rs