Tokio basics

Awaiting a task will block until it's complete. In the following code: x is first completed, then y

let x = some_task().await;
let y = some_other_task().await;

Runtime (you need one)

To create a runtime use

async some_task() -> u32 {

let runtime = tokio::runtime::Runtime::new().unwrap();
let value = runtime.block_on(some_task());

Spawning tasks and join

spawn can be considered analogous to std::thread::spawn. Spawning a task returns a Future (that can be awaited). There is no guarantee that a spawned tasks ends up on the same thread as another spawned task (unless it's on a single threaded runtime)

async get_number() -> u32 {

async no_spawn() -> *const () {

async fn use_join() -> u32 {
    // join three futures
    let (a, b, c) = tokio::join!(get_number(), get_number(), no_spawn());
    a + b

Networking comments

For reading / writing use tokio::io::{AsyncReadExt, AsyncWriteExt}.

async fn handle_socket(mut s: TcpStream) -> io::Result<()> {
    let mut buf = [0u8; 64];
    let res = s.read(&mut buf).await?;