Skip to main content
tokio-macros provides Tokio’s procedural macros that simplify setting up and testing async code. These macros eliminate boilerplate when creating async entry points and tests.

Installation

The macros are re-exported by the main tokio crate, so you typically don’t need to add tokio-macros directly:
Cargo.toml
The macros feature flag is required to use #[tokio::main] and #[tokio::test].

The #[tokio::main] Macro

The #[tokio::main] macro transforms an async main function into a synchronous one that runs on the Tokio runtime.

Basic Usage

This expands to:
The macro handles runtime setup automatically, making async code more ergonomic.

Runtime Flavors

Multi-threaded Runtime (Default)

The default flavor uses multiple threads:

Configure Worker Threads

By default, the number of worker threads equals the number of CPU cores.

Current Thread Runtime

Use a single-threaded runtime:
This expands to:

Multi-threaded with Explicit Flavor

Advanced Configuration

Start with Time Paused

Requires the test-util feature flag.

Custom Crate Name

If you’ve renamed the tokio crate:

The #[tokio::test] Macro

The #[tokio::test] macro enables writing async tests without manual runtime setup.

Basic Test

This expands to:

Multi-threaded Tests

Configure Test Workers

Tests with Paused Time

Requires the test-util feature.
Starting tests with paused time is extremely useful for testing time-dependent code without actual delays.

Complete Examples

Simple HTTP Server

Testing Async Functions

Current Thread for Deterministic Tests

Comparison Table

Purpose

Runtime Flavors Comparison

  • Best for: Production applications, CPU-bound tasks
  • Features: Work-stealing scheduler, scales with CPU cores
  • Trade-off: More overhead than current-thread
  • Best for: Tests, single-threaded apps, WASM
  • Features: Lightweight, deterministic execution
  • Trade-off: Cannot utilize multiple cores

When to Use Each Macro

Use #[tokio::main]

  • Application entry points
  • Production services
  • When you need full runtime control

Use #[tokio::test]

  • Unit tests for async code
  • Integration tests
  • When testing time-based logic

Common Patterns

Error Handling

Non-main Async Functions

You can use #[tokio::main] on non-main functions, but this creates a new runtime each time:
Using #[tokio::main] on frequently-called functions is inefficient. Consider using Runtime::block_on or passing runtime handles instead.

Requirements

1

Enable macros feature

Add macros to your Tokio features in Cargo.toml
2

Choose runtime flavor

Use rt-multi-thread for multi-threaded or rt for current-thread
3

Add test-util for testing

Include test-util feature for start_paused and time control

Utility Macros

The macros feature also includes essential utility macros for working with multiple concurrent operations.

The join! Macro

Waits on multiple concurrent branches, returning when all branches complete.
join! runs all futures concurrently on the same task and waits for all to complete, even if some return errors.

Fairness Control

By default, join! rotates which future is polled first. Use biased; for deterministic order:
Use join! when you need results from multiple independent operations and all must complete.

The try_join! Macro

Similar to join!, but returns early on the first Err:
All futures in try_join! must return Result with the same error type.

When to Use try_join!

The select! Macro

Waits on multiple concurrent branches, returning when the first branch completes:
select! cancels the remaining branches when the first one completes. Ensure operations are cancellation-safe.

Pattern Matching

Conditional Branches

Biased Polling

Biased polling can cause starvation. Place high-priority operations first, but ensure fairness for all branches.

The else Branch

Without an else branch, select! panics if all branches are disabled.

Cancellation Safety

Some operations are cancellation-safe, others are not: Cancellation-safe operations:
  • tokio::sync::mpsc::Receiver::recv
  • tokio::sync::broadcast::Receiver::recv
  • tokio::sync::watch::Receiver::changed
  • tokio::net::TcpListener::accept
  • tokio::io::AsyncReadExt::read (on TcpStream)
NOT cancellation-safe:
  • tokio::io::AsyncWriteExt::write_all
  • tokio::io::AsyncReadExt::read_exact

Comparison: join! vs try_join! vs select!

Complete Example: Using All Three

Resources

API Documentation

Complete API reference on docs.rs

Tokio Runtime

Learn more about runtime configuration

GitHub Repository

View source code

Testing Guide

Best practices for testing async code