TAIT Migration#
When impl_trait_in_assoc_type (rust-lang/rust#63063) stabilizes, future_form can migrate from boxing to zero-cost dispatch. A working prototype was built and tested on nightly (2026-02-24).
Status#
Blocked on the new trait solver (-Znext-solver, rust-lang/rust#107374). Nightly-only as of Rust 1.93 / Feb 2026.
What Changes#
FutureForm becomes a pure marker trait โ no GAT, no FromFuture, no boxing:
// Before (boxing)
pub trait FutureForm {
type Future<'a, T: 'a>: Future<Output = T> + 'a;
fn from_future<'a, T, F>(f: F) -> Self::Future<'a, T>
where Self::Future<'a, T>: FromFuture<'a, T, F>;
}
// After (TAIT)
pub trait FutureForm {}
The #[future_form] macro generates per-method TAIT associated types instead of boxing:
// Generated by macro โ no heap allocation
impl Counter<Sendable> for Memory {
type next<'fut_> = impl Future<Output = u32> + Send + 'fut_;
fn next(&self) -> Self::next<'_> {
async move { self.val + 1 }
}
}
A companion #[future_form_trait] macro desugars async fn in trait definitions:
#[future_form_trait]
trait Counter<K: FutureForm> {
async fn next(&self) -> u32;
}
// Expands to:
trait Counter<K: FutureForm> {
type next<'fut_>: Future<Output = u32> + 'fut_ where Self: 'fut_;
fn next(&self) -> Self::next<'_>;
}
What Doesn't Change#
The K: FutureForm generic parameter and #[future_form(Sendable, Local)] attribute syntax are preserved. Downstream code parameterized over K: FutureForm should not need changes beyond replacing K::from_future(async { ... }) with async fn / async move { ... }.
Why Not Now#
impl_trait_in_assoc_typeis unstable and may change- Nightly-only excludes most library consumers
- Boxing cost is acceptable for async I/O workloads
- Stable Rust support matters more for adoption
The current boxing implementation is forward-compatible: migration is mechanical when the feature stabilizes.