17
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Rustで非同期処理するとき、ランタイム選びで迷いませんか?

tokio vs async-std

この2つが二大巨頭。でも「どっち使えばいいの?」って聞かれると意外と答えにくい。

本気で比較してみました。

目次

  1. それぞれの特徴
  2. API比較
  3. パフォーマンス
  4. エコシステム
  5. 使い分けの指針
  6. まとめ

それぞれの特徴

tokio

[dependencies]
tokio = { version = "1", features = ["full"] }

特徴:

  • 最も人気: crates.io のダウンロード数が圧倒的
  • 高機能: ファイルI/O、タイマー、同期プリミティブなど全部入り
  • 高パフォーマンス: 大量の並行接続に対応
  • エコシステムが充実: axum, tonic, reqwest など
#[tokio::main]
async fn main() {
    println!("Hello, tokio!");
}

async-std

[dependencies]
async-std = { version = "1", features = ["attributes"] }

特徴:

  • std ライクなAPI: 標準ライブラリに近い設計
  • 学習しやすい: std を知ってれば使える
  • シンプル: 必要最低限の機能
#[async_std::main]
async fn main() {
    println!("Hello, async-std!");
}

API比較

ファイル読み込み

tokio

use tokio::fs::File;
use tokio::io::AsyncReadExt;

async fn read_file() -> std::io::Result<String> {
    let mut file = File::open("hello.txt").await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

async-std

use async_std::fs::File;
use async_std::io::ReadExt;

async fn read_file() -> std::io::Result<String> {
    let mut file = File::open("hello.txt").await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

ほぼ同じ! async-std は std のAPIに合わせてるので、移行が楽。

スリープ

tokio

use tokio::time::{sleep, Duration};

async fn wait() {
    sleep(Duration::from_secs(1)).await;
}

async-std

use async_std::task;
use std::time::Duration;

async fn wait() {
    task::sleep(Duration::from_secs(1)).await;
}

タスク生成

tokio

let handle = tokio::spawn(async {
    // 非同期処理
    42
});
let result = handle.await.unwrap();

async-std

let handle = async_std::task::spawn(async {
    // 非同期処理
    42
});
let result = handle.await;

Mutex

tokio

use tokio::sync::Mutex;

let mutex = Mutex::new(0);
let mut guard = mutex.lock().await;
*guard += 1;

async-std

use async_std::sync::Mutex;

let mutex = Mutex::new(0);
let mut guard = mutex.lock().await;
*guard += 1;

チャネル

tokio

use tokio::sync::mpsc;

let (tx, mut rx) = mpsc::channel(32);
tx.send(42).await.unwrap();
let value = rx.recv().await;

async-std

use async_std::channel;

let (tx, rx) = channel::bounded(32);
tx.send(42).await.unwrap();
let value = rx.recv().await;

パフォーマンス

ベンチマーク(目安)

シナリオ tokio async-std
大量の短いタスク
長時間のI/O
メモリ使用量
起動時間

結論: 大きな差はない。実用上はどちらでもOK。

tokio の強み

  • io_uring サポート(Linux 5.1+)
  • work-stealing スケジューラ
  • 細かいチューニング可能
// ランタイムのカスタマイズ
let runtime = tokio::runtime::Builder::new_multi_thread()
    .worker_threads(4)
    .enable_all()
    .build()
    .unwrap();

async-std の強み

  • シンプルなAPI
  • std との互換性重視
  • 学習コストが低い

エコシステム

tokio ベースのクレート

クレート 用途
axum Webフレームワーク
reqwest HTTPクライアント
tonic gRPC
sqlx データベース
hyper 低レベルHTTP
tower ミドルウェア

async-std ベースのクレート

クレート 用途
tide Webフレームワーク
surf HTTPクライアント
async-tungstenite WebSocket

現実

tokio のエコシステムが圧倒的に大きい

新しいクレートはだいたい tokio 対応が先。async-std は後からか、対応されないこともある。

ランタイム非依存のクレート

# 両方で使える
futures = "0.3"        # Future ユーティリティ
async-trait = "0.1"    # async トレイトメソッド

使い分けの指針

tokio を選ぶべき場合

Webサーバー/APIを作る

  • axum, actix-web, warp など主要フレームワークが tokio ベース

gRPC を使う

  • tonic は tokio 専用

既存の tokio ベースのクレートを使う

  • reqwest, sqlx など

高パフォーマンスが必要

  • 大量の並行接続を捌く

チームで開発

  • 情報量が多く、困ったときに調べやすい

async-std を選ぶべき場合

std に近いAPIが好み

  • 学習コストを抑えたい

シンプルな非同期処理だけ

  • 複雑な機能は不要

tide/surf を使いたい

  • async-std ベースの Webスタック

tokio に依存したくない

  • 何らかの理由で

実際のところ

迷ったら tokio

理由:

  1. エコシステムが大きい
  2. 情報が多い
  3. 困ったときに解決策が見つかりやすい
  4. 採用実績が多い

混在させたい場合

async-compat

両方のコードを混ぜて使える:

[dependencies]
async-compat = "0.2"
use async_compat::Compat;

// async-std のコードを tokio で動かす
#[tokio::main]
async fn main() {
    Compat::new(async_std_function()).await;
}

両対応のクレート

一部のクレートは両方に対応:

# sqlx: 両方対応
sqlx = { version = "0.7", features = ["runtime-tokio-native-tls"] }
# または
sqlx = { version = "0.7", features = ["runtime-async-std-native-tls"] }

比較まとめ

項目 tokio async-std
人気
エコシステム
API設計 独自 std風
学習コスト やや高
パフォーマンス
情報量
柔軟性

まとめ

結論

2025年現在、特に理由がなければ tokio を選ぶ

  • エコシステムの大きさ
  • 情報の多さ
  • 採用実績

これらで tokio が圧倒的。

async-std を選ぶ理由があるなら

  • std風のAPIが好き
  • tide/surf を使いたい
  • シンプルさ重視

それは全然アリ。性能差はほぼない。

チェックリスト

  • 使いたいクレートが何ベースか確認
  • チームの慣れを考慮
  • 迷ったら tokio

今すぐできるアクション

  1. 作りたいものに必要なクレートを調べる
  2. そのクレートが tokio/async-std どちらベースか確認
  3. それに合わせてランタイムを選ぶ

ランタイム選び、最初は悩むけど、実際はあまり大きな問題じゃないです。どっちを選んでも非同期Rustは書けます。

この記事が役に立ったら、いいね・ストックしてもらえると嬉しいです!

17
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?