๐Ÿฆ€๐Ÿš€ Abstract over Send and !Send traits crates.io/crates/future_form
0

Configure Feed

Select the types of activity you want to include in your feed.

future_form / design / tait-migration.md
2.1 kB

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_type is 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.