future_form 0.3.1

Abstractions over Send and !Send futures
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# `future_form`

[![crates.io](https://img.shields.io/crates/v/future_form.svg)](https://crates.io/crates/future_form)
[![CI](https://ci.hel.subduction.keyhive.org/api/badges/2/status.svg)](https://ci.hel.subduction.keyhive.org/repos/2)
[![docs.rs](https://img.shields.io/docsrs/future_form)](https://docs.rs/future_form)
[![License](https://img.shields.io/crates/l/future_form.svg)](https://codeberg.org/expede/future_form)

> "This isn't even my ~~final~~ _future_ form!"

Abstractions over `Send` and `!Send` futures in Rust.

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
future_form = "0.3.1"
```

## Motivation

Async Rust has a fragmentation problem. Some runtimes demand `Send` futures (tokio, async-std), while others are perfectly happy with `!Send` (single-threaded executors, Wasm). Library authors face an unpleasant choice:

1. Duplicate async trait implementations for both variants — `MyService` _and_ `MyLocalService`, all the way down
2. Force everyone onto `Send` futures, leaving `!Send` use cases out in the cold (or vice versa)
3. Maintain separate crates or feature flags for each variant

All of these are verbose, error-prone, and a maintenance headache.

Async Rust developers have long struggled with a question as old as time: _"Should I `Send` or `!Send`?"_ Finally, you can stop asking and simply... embrace your future form and delay to the final concrete call site.

## Approach

`future_form` solves this with a simple abstraction: write your async code once, parameterized over the "form" of future you need. The choice between `Send` and `!Send` becomes a type parameter that flows through your code.

```rust
use future_form::{FutureForm, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture};

// Define your trait once, generic over the future kind
pub trait Service<K: FutureForm> {
    fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
}

struct MyService;

// Implement for both Send and !Send with minimal boilerplate
impl Service<Local> for MyService {
    fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
        Local::from_future(async move { x * 2 })
    }
}

impl Service<Sendable> for MyService {
    fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
        Sendable::from_future(async move { x * 2 })
    }
}
```

Users pick the variant they need:

```rust
use future_form::{FutureForm, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture};

trait Service<K: FutureForm> {
    fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
}

struct MyService;

impl Service<Local> for MyService {
    fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
        Local::from_future(async move { x * 2 })
    }
}

impl Service<Sendable> for MyService {
    fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
        Sendable::from_future(async move { x * 2 })
    }
}

// For Send-required runtimes like tokio
async fn run_sendable(service: &impl Service<Sendable>) {
    let result = service.handle(42).await;
}

// For !Send runtimes like Wasm or single-threaded executors
async fn run_local(service: &impl Service<Local>) {
    let result = service.handle(42).await;
}
```

Or thread through the `FutureForm` parameter and delay the choice to compile time. This is typesafe: if you try to send a `Local` future between threads, you'll get a compile error telling you to specialize to `Sendable`.

```rust
use future_form::{FutureForm, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture};

trait Service<K: FutureForm> {
    fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
}

struct MyService;

impl Service<Local> for MyService {
    fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
        Local::from_future(async move { x * 2 })
    }
}

impl Service<Sendable> for MyService {
    fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
        Sendable::from_future(async move { x * 2 })
    }
}

async fn run<K: FutureForm>(service: &impl Service<K>) {
    let result = service.handle(42).await;
}
```

## Choose Your Form

### `FutureForm` Trait

The core abstraction — a trait with an associated future type:

```rust,no_run
use std::future::Future;

pub trait FutureForm {
    type Future<'a, T: 'a>: Future<Output = T> + 'a;
    // + from_future() and ready() default methods
}
```

This library ships with `Sendable` and `Local`, but you can implement your own forms for other boxing strategies or even unboxed futures.

### `Sendable`

> _"Have future, will travel"_

Represents `Send` futures, backed by `futures::future::BoxFuture`. For multithreaded [^over9k] contexts:

```rust,ignore
impl FutureForm for Sendable {
    type Future<'a, T: 'a> = BoxFuture<'a, T>;
}
```

[^over9k]: Thread level over 9000?!

### `Local`

> _What happens on the thread, stays on the thread._

Represents `!Send` futures, backed by `futures::future::LocalBoxFuture`:

```rust,ignore
impl FutureForm for Local {
    type Future<'a, T: 'a> = LocalBoxFuture<'a, T>;
}
```

### `#[future_form]` Macro

> _One impl to rule them all._

Rust's `async { ... }` blocks have a concrete `Send` or `!Send` type, so you can't write a single generic `impl` that works for both, nor can you extract out the body without incurring those bounds. Without this macro, if you want identical behavior, Rust forces you to write and maintain identical implementations for each variant. This macro lets you write your `impl` once and generates one for each future form you pass in:

```rust,ignore
use std::marker::PhantomData;
use future_form::{future_form, FutureForm};

trait Counter<K: FutureForm> {
    fn next(&self) -> K::Future<'_, u32>;
}

struct Memory<K> {
    val: u32,
    _marker: PhantomData<K>,
}

// Generates impl Counter<Sendable> and impl Counter<Local>
#[future_form(Sendable, Local)]
impl<K: FutureForm> Counter<K> for Memory<K> {
    fn next(&self) -> K::Future<'_, u32> {
        let val = self.val;
        K::from_future(async move { val + 1 })
    }
}
```

You can also generate only specific variants:

```rust,ignore
#[future_form(Sendable)]        // Only Sendable
#[future_form(Local)]           // Only Local
#[future_form(Sendable, Local)] // Both
```

Each variant can have its own additional bounds using `where`:

```rust,ignore
#[future_form(Sendable where T: Send, Local where T: Debug)]
impl<K: FutureForm, T: Clone> Processor<K> for Container<T> {
    fn process(&self) -> K::Future<'_, T> {
        let value = self.value.clone();
        K::from_future(async move { value })
    }
}
// Generates:
//   impl<T: Clone + Send> Processor<Sendable> for Container<T>
//   impl<T: Clone + Debug> Processor<Local> for Container<T>
```

## Threading `FutureForm` Through Your Code

The trick is structuring your code so the `FutureForm` parameter flows naturally through your API. Two common patterns:

1. **Return the future directly** — the `K` appears in the return type
2. **Carry `K` in a struct**`PhantomData<K>` lets you thread it through methods

Here's the struct approach:

```rust
use std::marker::PhantomData;
use future_form::{FutureForm, Local, Sendable};
use futures::future::{BoxFuture, LocalBoxFuture};

trait Service<K: FutureForm> {
    fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
}

struct MyService;

impl Service<Local> for MyService {
    fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
        Local::from_future(async move { x * 2 })
    }
}

impl Service<Sendable> for MyService {
    fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
        Sendable::from_future(async move { x * 2 })
    }
}

pub struct Handler<K: FutureForm> {
    service: MyService,
    _marker: PhantomData<K>,
}

impl<K: FutureForm> Handler<K>
where
    MyService: Service<K>
{
    pub fn new(service: MyService) -> Self {
        Self {
            service,
            _marker: PhantomData,
        }
    }

    // K is part of Self, so methods can use it naturally
    pub async fn process(&self, x: u8) -> u8 {
        Service::<K>::handle(&self.service, x).await
    }
}

# async fn example() {
// Usage is clean and type-safe
let my_service = MyService;
let handler = Handler::<Sendable>::new(my_service);
let result = handler.process(42).await;
# }
```

Or when returning futures directly:

```rust,ignore
// K appears in the return type
pub fn create_task<K: FutureForm>(x: u8) -> K::Future<'static, u8>
where
    K::Future<'static, u8>: FromFuture<'static, u8, impl Future<Output = u8>>
{
    K::from_future(async move { x * 2 })
}
```

The `FutureForm` choice propagates through your entire call stack — type safety all the way down.

### Ready Values

When you already have a computed value and just need to wrap it as a future, use `K::ready()` instead of `K::from_future(async { value })`:

```rust,ignore
fn cached_count<K: FutureForm>(&self) -> K::Future<'_, u32>
where
    K::Future<'_, u32>: FromReady<'_, u32>,
{
    K::ready(self.count)
}
```

This avoids creating an unnecessary async state machine for synchronous results.

### Host-Driven Polling (FFI)

> _No runtime? No problem._

The companion [`future_form_ffi`](https://crates.io/crates/future_form_ffi) crate lets FFI hosts (Go, Java, Python, C, Swift) drive Rust async state machines without an async runtime. Your trait impls _don't change_ — the same `#[future_form(Sendable)]` implementation works whether a Rust runtime `.await`s the future or a foreign host polls it in a loop.

See the [`future_form_ffi` docs](https://docs.rs/future_form_ffi) and the [FFI examples](../future_form_ffi/examples/) for complete Go, Java, and Python hosts driving the same Rust counter through C ABI and JNI.

## Use Cases

| Use Case | Description |
|----------|-------------|
| Cross-platform libraries | Write async traits once, support both native and Wasm targets |
| Runtime flexibility | Allow users to choose their async runtime without forcing `Send` constraints |
| Testing | Use `Local` futures in single-threaded test environments while production uses `Sendable` |
| Gradual migration | Support both variants during migration between runtimes |

## Design Philosophy

For deeper architectural discussion, see the [`design/`](../design/) notes.

`future_form` is deliberately minimal:

1. **Near-zero-cost abstraction** — same overhead as `async-trait` or `tokio::spawn`: one heap allocation per async call
2. **Compile-time dispatch**`Send` vs `!Send` is resolved statically, no runtime overhead
3. **Small API surface** — one core trait, two marker types, one macro. That's it.
4. **Plays well with others** — builds on the `futures` crate's existing types
5. **Extensible** — implement `FutureForm` for your own types if the builtins don't fit
6. **FFI-ready** — the `PollOnce` trait lets foreign hosts drive any boxed future without an async runtime

## Comparison with `async-trait`

[`async-trait`](https://crates.io/crates/async-trait) and `future_form` are complementary — they solve different problems:

| Aspect | `async-trait` | `future_form` |
|--------|---------------|----------------|
| **Problem solved** | Async methods in traits (pre-RPITIT) | Abstracting over Send vs !Send |
| **Send/!Send choice** | Per-trait (`#[async_trait(?Send)]`) | Per-usage site (`K: FutureForm`) |
| **When to choose** | At trait definition time | At impl usage time |
| **Post-RPITIT (Rust 1.75+)** | Less necessary for basic cases | Still useful for Send/!Send flexibility |

### When to use `async-trait`

- You need async trait methods on older Rust versions (pre-1.75)
- You want the simplest possible syntax for async traits
- All users of your trait will use the same Send/!Send variant

### When to use `future_form`

- You want _consumers_ to choose Send vs !Send at usage time
- You're building a library that should work in both multi-threaded and single-threaded contexts
- You need both variants available simultaneously (e.g., for different runtime configurations)

### Using both together

Nothing stops you from using both. A common pattern: `async-trait` for internal convenience, `future_form` at your public API boundary:

```rust,ignore
use async_trait::async_trait;
use future_form::{FutureForm, future_form};

// Internal trait using async-trait for convenience
#[async_trait]
trait InternalService {
    async fn fetch(&self) -> Vec<u8>;
}

// Public trait using future_form for flexibility
trait PublicService<K: FutureForm> {
    fn fetch(&self) -> K::Future<'_, Vec<u8>>;
}
```

## Comparison with `trait-variant`

[`trait-variant`](https://crates.io/crates/trait-variant) is the Rust lang team's approach: generate a _second trait_ with `Send` bounds from your base trait. Different philosophy, different tradeoffs:

| Aspect | `trait-variant` | `future_form` |
|--------|-----------------|----------------|
| **Approach** | Generates two separate traits | Single trait with type parameter |
| **Syntax** | Native `async fn` | Boxed futures via `K::Future` |
| **Trait count** | Two traits (`Local*` and `Send` variant) | One trait generic over `K` |
| **Impl pattern** | Separate impl block per variant | Single impl with `#[future_form]` generates both |
| **Middleware pattern** | Limited (can't conditionally impl Send) | Supported via generic `K` propagation |

### `trait-variant` example

```rust,ignore
#[trait_variant::make(HttpService: Send)]
pub trait LocalHttpService {
    async fn fetch(&self, url: &str) -> Vec<u8>;
}

// Implementors write separate impl blocks:
impl LocalHttpService for MyClient { ... }
impl HttpService for MyClient { ... }  // must duplicate impl body
```

### `future_form` example

```rust,ignore
pub trait HttpService<K: FutureForm> {
    fn fetch(&self, url: &str) -> K::Future<'_, Vec<u8>>;
}

// Implementors can support BOTH via #[future_form]:
#[future_form(Sendable, Local)]
impl<K: FutureForm> HttpService<K> for MyClient {
    fn fetch(&self, url: &str) -> K::Future<'_, Vec<u8>> {
        K::from_future(async move { /* ... */ })
    }
}
```

### When to use `trait-variant`

- You want native `async fn` syntax (can avoid boxing in some contexts)
- You're okay with separate impl blocks for each variant
- You prefer the Rust lang team's officially recommended approach

### When to use `future_form`

- You want a single trait to support _both_ `Send` and `!Send` usage
- You're building middleware/wrappers that should preserve the caller's Send choice
- You need the `K: FutureForm` parameter to propagate through your type hierarchy

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]https://codeberg.org/expede/future_form/src/branch/main/LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT]https://codeberg.org/expede/future_form/src/branch/main/LICENSE-MIT or <http://opensource.org/licenses/MIT>)

at your option.