Rustの'error[E0706]: functions in traits cannot be declared async'をトレイトで非同期メソッドを定義する際に修正する方法

intermediate🦀 Rust2026-05-21| Rust 1.x(stable)、任意のOS(Linux、macOS、Windows)— nightlyフィーチャーやasync-traitクレートなしでトレイト定義内に`async fn`を使用した場合に発生

Error Message

error[E0706]: functions in traits cannot be declared async
#async#trait#async-trait#tokio

TL;DR

Rustの安定版コンパイラでは、トレイト定義の中に直接async fnを書くことができません。最も手軽な解決策は、async-traitクレートを追加し、トレイトとすべてのimplブロックの両方に#[async_trait]を付けることです。

# Cargo.toml
[dependencies]
async-trait = "0.1"
tokio = { version = "1", features = ["full"] }

use async_trait::async_trait;

#[async_trait]
trait Fetcher {
    async fn fetch(&self, url: &str) -> String;
}

struct HttpFetcher;

#[async_trait]
impl Fetcher for HttpFetcher {
    async fn fetch(&self, url: &str) -> String {
        format!("fetched: {}", url)
    }
}

このエラーが発生する原因

トレイト内にasyncメソッドを書くと、コンパイラが処理を止めてしまいます:

trait Fetcher {
    async fn fetch(&self, url: &str) -> String; // error[E0706]
}

エラーの全文:

error[E0706]: functions in traits cannot be declared async
 --> src/main.rs:2:5
  |
2 |     async fn fetch(&self, url: &str) -> String;
  |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     `async` because of this
  |
  = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
  = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information

なぜこうなるのかを説明します。async fnを書くと、Rustは裏側で戻り値の型として匿名のimpl Futureを生成します。トレイトでは、この匿名型が実装ごとに異なります。安定版Rustにはこれをトレイトのシグネチャで表現する手段がありません。これはdyn Traitのオブジェクト安全性ルールに深く関係しています。この機能(RPITIT — Return Position Impl Trait In Traits)は安定版への導入までに何年もかかり、Rust 1.75でようやくリリースされました。

修正方法1: async-traitクレートを使う(ほぼすべてのケースで有効)

Axum、Tower、そして非同期Rustエコシステムの大部分がこのアプローチを採用しています。実績のある方法です。

ステップ1: 依存関係を追加する:

cargo add async-trait

ステップ2: インポートし、トレイトとすべてのimplブロックの両方にアノテーションを付ける:

use async_trait::async_trait;

#[async_trait]
trait Database {
    async fn query(&self, sql: &str) -> Vec<String>;
    async fn execute(&self, sql: &str) -> bool;
}

struct Postgres;

#[async_trait]
impl Database for Postgres {
    async fn query(&self, sql: &str) -> Vec<String> {
        // 実際の実装をここに書く
        vec![sql.to_string()]
    }

    async fn execute(&self, sql: &str) -> bool {
        true
    }
}

内部の仕組みとして、async-traitはasyncメソッドをPin<Box<dyn Future + Send>>を返すように書き換えます。このヒープアロケーションがdyn Traitと組み合わせて動作できる理由です。わずかな実行時コストがありますが、ほとんどのアプリケーションでは無視できる程度です。

dyn Traitでも動作する

use async_trait::async_trait;

#[async_trait]
trait Notifier {
    async fn notify(&self, message: &str);
}

struct EmailNotifier;

#[async_trait]
impl Notifier for EmailNotifier {
    async fn notify(&self, message: &str) {
        println!("Email: {}", message);
    }
}

async fn send_notification(notifier: &dyn Notifier, msg: &str) {
    notifier.notify(msg).await;
}

修正方法2: impl Futureを手動で返す(追加依存なし)

追加の依存関係を避けたい場合やdyn Traitが不要な場合は、明示的なimpl Futureを返す方法があります。これはRPITITの安定化の一環として、Rust 1.75で安定版に導入されました。

use std::future::Future;

trait Fetcher {
    fn fetch(&self, url: String) -> impl Future<Output = String> + Send;
}

struct HttpFetcher;

impl Fetcher for HttpFetcher {
    fn fetch(&self, url: String) -> impl Future<Output = String> + Send {
        async move { format!("fetched: {}", url) }
    }
}

重要な制限が一つあります:このトレイトをdyn Fetcherとして使用することはできません。ジェネリクスによる静的ディスパッチでのみ機能します。実行時のポリモーフィズムが必要な場合は、修正方法1に戻ってください。

修正方法3: Nightly — トレイト内のネイティブ async fn

Nightly版Rustでは、フィーチャーフラグを使うことで、クレートなしに直接asyncトレイトメソッドを書くことができます:

#![feature(async_fn_in_trait)]

trait Fetcher {
    async fn fetch(&self, url: &str) -> String;
}

本番コードではこの方法は避けてください。Nightlyのフィーチャーはコンパイラのバージョン間で予告なく壊れることがあります。安定版では修正方法1または修正方法2を使用してください。

どの修正方法を使うべきか?

  • dyn Trait(実行時ディスパッチ)が必要?async-traitクレート(修正方法1)
  • 静的ディスパッチのみ、Rust 1.75以上? → トレイト内のimpl Future(修正方法2)— 追加依存なし
  • Nightlyで試したい? → フィーチャーフラグを使ったネイティブasync fn in trait(修正方法3)

修正の確認

クリーンビルドを実行する:

cargo build
# 期待される結果: エラーなし、コンパイル成功

コード生成をスキップした素早いチェック:

cargo check

Tokioで非同期テストを実行する:

cargo test

よくある間違い: implブロックへの#[async_trait]付け忘れ

async-traitを追加した後、トレイト定義にだけアノテーションを付けてimplブロックを忘れるのは、最もよくある二次エラーです:

// 間違い — implブロックにアトリビュートがない
#[async_trait]
trait Worker {
    async fn run(&self);
}

struct MyWorker;

impl Worker for MyWorker { // エラー!
    async fn run(&self) {}
}

すべてのimplブロックに — トレイトだけでなく — #[async_trait]が必要です。一つでも忘れると、コンパイラが教えてくれます。

参考リンク

Related Error Notes