Sửa lỗi Rust 'error[E0706]: functions in traits cannot be declared async' Khi Định Nghĩa Async Methods Trong Traits

intermediate🦀 Rust2026-05-21| Rust 1.x (stable), mọi hệ điều hành (Linux, macOS, Windows) — xảy ra khi dùng `async fn` bên trong định nghĩa trait mà không có nightly features hoặc async-trait crate

Error Message

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

TL;DR

Trình biên dịch Rust stable không cho phép viết async fn trực tiếp bên trong định nghĩa trait. Cách sửa nhanh nhất: thêm crate async-trait và gắn #[async_trait] lên cả trait lẫn mọi block impl.

# 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)
    }
}

Nguyên Nhân Gây Ra Lỗi

Viết một async method bên trong trait và trình biên dịch sẽ dừng lại:

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

Toàn bộ thông báo lỗi:

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

Đây là lý do xảy ra. Khi bạn viết async fn, Rust sẽ tự động tạo ra một impl Future ẩn danh làm kiểu trả về bên dưới. Trong một trait, kiểu ẩn danh đó khác nhau ở mỗi implementation — và Rust stable không có cách nào biểu diễn điều đó trong chữ ký của trait. Vấn đề này liên quan đến quy tắc object safety của dyn Trait. Tính năng theo dõi điều này (RPITIT — Return Position Impl Trait In Traits) mất nhiều năm mới hoàn thiện và chỉ được đưa vào stable từ Rust 1.75.

Cách Sửa 1: Dùng Crate async-trait (Hoạt Động Trong Hầu Hết Mọi Trường Hợp)

Axum, Tower và phần lớn hệ sinh thái async Rust đều dùng cách này. Đã được kiểm chứng thực tế.

Bước 1: Thêm dependency:

cargo add async-trait

Bước 2: Import và gắn annotation lên cả trait lẫn mọi block 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> {
        // thực hiện thực tế ở đây
        vec![sql.to_string()]
    }

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

Cơ chế bên trong: async-trait viết lại các async method của bạn để trả về Pin<Box<dyn Future + Send>>. Việc cấp phát trên heap đó chính là điều cho phép nó hoạt động với dyn Trait. Có một chi phí runtime nhỏ, nhưng với hầu hết ứng dụng thì không đáng kể.

Hoạt Động Cả Với 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;
}

Cách Sửa 2: Trả Về impl Future Thủ Công (Không Cần Dependency)

Không muốn thêm dependency? Không cần dyn Trait? Hãy trả về một impl Future tường minh. Cách này đã có trên stable từ Rust 1.75 như một phần của quá trình ổn định hóa RPITIT.

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) }
    }
}

Một giới hạn cứng: bạn không thể dùng trait này dưới dạng dyn Fetcher. Cách này chỉ hoạt động với static dispatch thông qua generics. Nếu cần runtime polymorphism, hãy quay lại Cách Sửa 1.

Cách Sửa 3: Nightly — Native async fn Trong Traits

Trên Rust nightly, một feature flag cho phép bạn viết async trait method trực tiếp mà không cần bất kỳ crate nào:

#![feature(async_fn_in_trait)]

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

Bỏ qua cách này cho code production. Các tính năng nightly có thể bị thay đổi giữa các phiên bản trình biên dịch mà không có cảnh báo. Hãy dùng Cách Sửa 1 hoặc Cách Sửa 2 trên stable.

Nên Dùng Cách Sửa Nào?

  • Cần dyn Trait (runtime dispatch)? → Crate async-trait (Cách Sửa 1)
  • Chỉ dùng static dispatch, Rust 1.75+?impl Future trong trait (Cách Sửa 2) — không cần dependency thêm
  • Thử nghiệm trên nightly? → Native async fn in trait với feature flag (Cách Sửa 3)

Xác Minh Bản Sửa

Chạy build sạch:

cargo build
# Kết quả mong đợi: không có lỗi, biên dịch thành công

Để kiểm tra nhanh hơn mà bỏ qua bước sinh code:

cargo check

Chạy các async test với Tokio:

cargo test

Lỗi Thường Gặp: Quên #[async_trait] Trên Các Block impl

Sau khi thêm async-trait, chỉ gắn annotation lên định nghĩa trait mà quên các block impl là lỗi tiếp theo phổ biến nhất:

// Sai — block impl thiếu attribute
#[async_trait]
trait Worker {
    async fn run(&self);
}

struct MyWorker;

impl Worker for MyWorker { // lỗi!
    async fn run(&self) {}
}

Mọi block impl — không chỉ riêng trait — đều cần có #[async_trait]. Bỏ sót một cái và trình biên dịch sẽ nhắc bạn ngay.

Đọc Thêm

  • Crate async-trait trên crates.io
  • Rust issue #91611 — theo dõi quá trình ổn định hóa async fn trong traits
  • Ghi chú phát hành Rust 1.75 — ổn định hóa RPITIT

Related Error Notes