Skip to main content
This quickstart guide will walk you through building a simple asynchronous application with Tokio. You’ll learn the basics of async programming, task spawning, and network I/O.

Prerequisites

Before starting, make sure you have:
  • Rust 1.71 or later installed
  • Basic familiarity with Rust syntax
  • Tokio added to your project (see Installation)

Hello, async world

Let’s start with the simplest possible Tokio program:
This example demonstrates several key concepts:
1

The #[tokio::main] macro

The #[tokio::main] macro sets up the async runtime and allows your main function to be async. This is the entry point for all Tokio applications.
2

Async/await syntax

The await keyword pauses execution until an async operation completes. Operations like connect() and write_all() return futures that must be awaited.
3

Asynchronous I/O

TcpStream is Tokio’s non-blocking TCP socket. All I/O operations are asynchronous and won’t block other tasks.
To test this example, first start a server with ncat -l 6142 in one terminal, then run your program in another.

Building a TCP echo server

Now let’s build something more practical: a TCP echo server that handles multiple concurrent connections.
This server showcases important Tokio patterns:

Accepting connections

The server binds to an address and continuously accepts incoming connections in a loop. Each accept() call waits asynchronously for a new connection.

Spawning tasks

The tokio::spawn function creates a new task that runs concurrently on the Tokio runtime. This allows the server to handle multiple clients simultaneously without blocking.
Each spawned task is lightweight (only a few hundred bytes) and can run on any thread in the runtime’s thread pool.

Reading and writing

AsyncReadExt and AsyncWriteExt provide async methods for reading from and writing to I/O streams.

Testing the server

1

Run the server

You should see:
2

Connect with telnet

In another terminal:
3

Send messages

Type any message and press Enter. The server will echo it back to you.

Working with channels

Tokio provides several channel types for communication between tasks. Here’s an example using the mpsc (multi-producer, single-consumer) channel:
Channels are useful for:
  • Sending messages between tasks
  • Coordinating work across multiple async operations
  • Implementing producer-consumer patterns

oneshot

Single-value channel for sending one message

mpsc

Multi-producer, single-consumer channel

broadcast

Multi-producer, multi-consumer with message cloning

Timeouts and intervals

Tokio’s time module provides utilities for working with time:

Timeouts

Intervals

Common patterns

Handling blocking operations

Sometimes you need to run CPU-intensive or blocking code. Use spawn_blocking to avoid blocking the async runtime:
Never run blocking operations directly in async code - it will prevent other tasks from making progress. Always use spawn_blocking for blocking work.

Graceful shutdown

Use tokio::select! to handle cancellation and graceful shutdown:

Next steps

You now have the fundamentals to build async applications with Tokio. Here are some resources to continue learning:

API documentation

Complete API reference with detailed examples

Tokio tutorial

In-depth guide covering advanced topics

Examples

Real-world examples from the Tokio repository

Discord community

Get help and connect with other Tokio users

Key takeaways

  • Use #[tokio::main] to set up the async runtime
  • Spawn tasks with tokio::spawn for concurrent execution
  • Use async I/O operations instead of blocking ones
  • Leverage channels for task communication
  • Use spawn_blocking for CPU-intensive work
  • Handle graceful shutdown with tokio::select!
Start simple and add complexity as needed. The full feature flag gives you access to all of Tokio’s capabilities while you’re learning.