bezhaile
# Fix incorrect dyn-compatibility claim for `trait-variant` in `async-fn-in-trait` ## Summary `rules/async-fn-in-trait.md` currently says that `#[trait_variant::make(RepoSend: Send)]` generates a variant that is dyn-compatible "via boxing", and shows: ```rust #[trait_variant::make(RepoSend: Send)] trait Repo { async fn get(&self, id: u64) -> anyhow::Result<String>; } fn make_repo() -> Box<dyn RepoSend> { # unimplemented!() } ``` That claim is incorrect. `trait-variant` adds the required `Send` bound to the returned future, but it does not box or type-erase that future. ## Why this matters The generated trait is effectively of the form: ```rust trait RepoSend: Send { fn get( &self, id: u64, ) -> impl Future<Output = anyhow::Result<String>> + Send; } ``` A method returning `impl Trait` is not dyn-compatible, so `Box<dyn RepoSend>` does not become valid merely because the returned future is `Send`. In other words: ```text Send bound != dyn compatibility ``` This is especially important in this repository because the rules are consumed by AI coding agents, and the contributing guide asks that examples compile on current stable Rust. ## Minimal reproduction ```rust #[trait_variant::make(RepoSend: Send)] trait Repo { async fn get(&self, id: u64) -> String; } fn use_dyn(_: Box<dyn RepoSend>) {} ``` The `Box<dyn RepoSend>` use is rejected because the generated method has an opaque `impl Future` return type. ## Suggested correction The rule should distinguish two separate concerns: 1. **Sendability**: `trait-variant` can generate a variant whose returned future is `Send`. 2. **Dynamic dispatch**: this still requires type erasure, for example `async-trait`, a manually boxed `Pin<Box<dyn Future<...>>>`, or another dyn adapter. So the caveat could say that `trait-variant` solves the `Send`-bound problem for native async trait methods, but does not by itself make the generated trait dyn-compatible. The "When to Use Each Approach" table should likewise avoid recommending `trait-variant` alone for `dyn Trait`. ## References - Rust Reference: dyn compatibility rules for traits, including opaque return types - Rust Async WG / Rust blog guidance on `async fn` in traits and return-position `impl Trait` - `trait-variant` crate documentation - `async-trait` crate documentation for boxed-future type erasure