From b1546008fc7ce99d8a143041732ed44b8dc2dc78 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 29 Apr 2026 15:29:29 -0700 Subject: [PATCH] docs: discourage `#[async_trait]` and `#[allow(async_fn_in_trait)]` (#20242) ## Why We have run into two avoidable problems when introducing async trait APIs in Rust: - `#[async_trait]` has caused materially worse build times in this repository. - `#[allow(async_fn_in_trait)]` makes it too easy to ship a public trait without spelling out whether the returned future is `Send`, which hides an important part of the trait contract. We already have a good example of the preferred alternative in [#16630](https://github.com/openai/codex/pull/16630) / [`3c7f013f9735`](https://github.com/openai/codex/commit/3c7f013f9735), but that guidance currently lives only as prior art in the codebase. This PR documents the rule in `AGENTS.md` so contributors are more likely to follow the native RPITIT pattern before these two shortcuts spread further. ## What Changed - added Rust guidance in `AGENTS.md` discouraging both `#[async_trait]` and `#[allow(async_fn_in_trait)]` - pointed contributors to the native RPITIT pattern with explicit `Send` bounds on the returned future - clarified that implementations may still use `async fn` when they satisfy that trait contract ## Verification - docs-only change; no tests run --- AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 23f0bab38..6939d146b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,12 @@ In the codex-rs folder where the rust code lives: - You can run `just argument-comment-lint` to run the lint check locally. This is powered by Bazel, so running it the first time can be slow if Bazel is not warmed up, though incremental invocations should take <15s. Most of the time, it is best to update the PR and let CI take responsibility for checking this (or run it asynchronously in the background after submitting the PR). Note CI checks all three platforms, which the local run does not. - When possible, make `match` statements exhaustive and avoid wildcard arms. - Newly added traits should include doc comments that explain their role and how implementations are expected to use them. +- Discourage both `#[async_trait]` and `#[allow(async_fn_in_trait)]` in Rust traits. + - Prefer native RPITIT trait methods with explicit `Send` bounds on the returned future, as in `3c7f013f9735` / `#16630`. + - Preferred trait shape: + `fn foo(&self, ...) -> impl std::future::Future + Send;` + - Implementations may still use `async fn foo(&self, ...) -> T` when they satisfy that contract. + - Do not use `#[allow(async_fn_in_trait)]` as a shortcut around spelling the future contract explicitly. - When writing tests, prefer comparing the equality of entire objects over fields one by one. - When making a change that adds or changes an API, ensure that the documentation in the `docs/` folder is up to date if applicable. - Prefer private modules and explicitly exported public crate API.