Select the types of activity you want to include in your feed.
Add `#[kinds]` macro and IntoFutureKind trait (#1)
Proc macro generates Sendable and Local trait impls from a single generic definition. Supports conditional bounds per variant. IntoFutureKind abstracts over boxing for extensibility.
···11+# Design Notes
22+33+## Performance Impact of Boxing Futures
44+55+`futures_kind` uses `BoxFuture` and `LocalBoxFuture` under the hood. This section discusses the performance implications of that choice.
66+77+### Costs
88+99+**Allocation**
1010+- One heap allocation per boxed future (typically small, but adds up in hot paths)
1111+- Deallocation when the future completes
1212+1313+**Indirection**
1414+- Virtual dispatch through the `dyn Future` trait object on each poll
1515+- Pointer chasing can hurt CPU cache locality
1616+1717+### In Practice
1818+1919+The impact is often negligible because:
2020+2121+1. Futures are usually polled relatively few times compared to the work they represent
2222+2. A single network call or disk read dwarfs the cost of one allocation
2323+3. The allocator is highly optimized for small, short-lived allocations
2424+2525+### When It Matters
2626+2727+- Tight loops spawning thousands of tiny futures
2828+- Latency-critical code paths (microsecond-level)
2929+- Embedded/`no_std` environments with constrained allocators
3030+3131+### Alternatives
3232+3333+| Approach | Tradeoff |
3434+|---------------------------------|-----------------------------------------------------------------------------------|
3535+| `async-trait` | Same cost (also boxes) |
3636+| `impl Future` (static dispatch) | Avoids boxing but can't be used in trait objects or with this kind of abstraction |
3737+| Enum-based dispatch | Works for a fixed set of variants, but doesn't generalize |
3838+3939+### Recommendation
4040+4141+For most async code doing I/O, the boxing cost is noise. If profiling shows it's a bottleneck, you'd typically avoid the abstraction entirely in that hot path rather than try to optimize the boxing.
···4455## Motivation
6677-Async Rust has a fragmentation problem: some runtimes require `Send` futures (like `tokio` and `async-std`), while others work with `!Send` futures (like single-threaded executors or WASM environments). This forces library authors to either:
77+Async Rust has a fragmentation problem: some runtimes require `Send` futures (like `tokio` and `async-std`), while others work with `!Send` futures (like single-threaded executors or Wasm environments). This forces library authors to either:
88991. Duplicate their async trait implementations for both `Send` and `!Send` variants, including all consumers (e.g. `MyService` and `MyLocalService`)
10102. Force all users to use `Send` futures, excluding legitimate `!Send` use cases
···7070 let result = service.handle(42).await;
7171}
72727373-// For !Send runtimes like WASM or single-threaded executors
7373+// For !Send runtimes like Wasm or single-threaded executors
7474async fn use_local(service: &impl Service<Local>) {
7575 let result = service.handle(42).await;
7676}
···113113114114The central abstraction that defines an associated type for futures:
115115116116-```rust,ignore
116116+```rust,no_run
117117pub trait FutureKind {
118118 type Future<'a, T: 'a>: Future<Output = T> + 'a;
119119}
···123123124124Represents `Send` futures, backed by `futures::future::BoxFuture`:
125125126126-```rust,ignore
126126+```rust,no_run
127127impl FutureKind for Sendable {
128128 type Future<'a, T: 'a> = BoxFuture<'a, T>;
129129}
···133133134134Represents `!Send` futures, backed by `futures::future::LocalBoxFuture`:
135135136136-```rust,ignore
136136+```rust,no_run
137137impl FutureKind for Local {
138138 type Future<'a, T: 'a> = LocalBoxFuture<'a, T>;
139139}
140140```
141141142142+### `#[kinds]` Macro
143143+144144+While you can define traits generic over `FutureKind`, Rust cannot verify that a single `async` block satisfies both `Send` and `!Send` bounds for an arbitrary `K: FutureKind`. The `#[kinds]` macro solves this by generating separate implementations for each variant, allowing the compiler to verify each one independently:
145145+146146+```rust,no_run
147147+use std::marker::PhantomData;
148148+use futures_kind::{kinds, FutureKind};
149149+150150+trait Counter<K: FutureKind> {
151151+ fn next(&self) -> K::Future<'_, u32>;
152152+}
153153+154154+struct Memory<K> {
155155+ val: u32,
156156+ _marker: PhantomData<K>,
157157+}
158158+159159+// Generates impl Counter<Sendable> and impl Counter<Local>
160160+#[kinds]
161161+impl<K: FutureKind> Counter<K> for Memory<K> {
162162+ fn next(&self) -> K::Future<'_, u32> {
163163+ let val = self.val;
164164+ Box::pin(async move { val + 1 })
165165+ }
166166+}
167167+```
168168+169169+You can also generate only specific variants:
170170+171171+```rust,no_run
172172+#[kinds(Sendable)] // Only Sendable
173173+#[kinds(Local)] // Only Local
174174+#[kinds(Sendable, Local)] // Both (default)
175175+```
176176+177177+Each variant can have its own additional bounds using `where`:
178178+179179+```rust,no_run
180180+#[kinds(Sendable where T: Send, Local where T: Debug)]
181181+impl<K: FutureKind, T: Clone> Processor<K> for Container<T> {
182182+ fn process(&self) -> K::Future<'_, T> {
183183+ let value = self.value.clone();
184184+ Box::pin(async move { value })
185185+ }
186186+}
187187+// Generates:
188188+// impl<T: Clone + Send> Processor<Sendable> for Container<T>
189189+// impl<T: Clone + Debug> Processor<Local> for Container<T>
190190+```
191191+142192## Threading `FutureKind` Through Your Code
143193144194The simplest pattern is to structure your code so the `FutureKind` type parameter appears naturally in your API. This typically happens when:
···203253204254Or when returning futures directly:
205255206206-```rust,ignore
256256+```rust,no_run
207257// K appears in the return type
208258pub fn create_task<K: FutureKind>(x: u8) -> K::Future<'static, u8>
209259where
···218268219269## Use Cases
220270221221-- **Cross-platform libraries**: Write async traits once, support both native and WASM targets
222222-- **Runtime flexibility**: Allow users to choose their async runtime without forcing `Send` constraints
223223-- **Testing**: Use `Local` futures in single-threaded test environments while production uses `Sendable`
224224-- **Gradual migration**: Support both variants during migration between runtimes
271271+| Use Case | Description |
272272+|----------|-------------|
273273+| Cross-platform libraries | Write async traits once, support both native and Wasm targets |
274274+| Runtime flexibility | Allow users to choose their async runtime without forcing `Send` constraints |
275275+| Testing | Use `Local` futures in single-threaded test environments while production uses `Sendable` |
276276+| Gradual migration | Support both variants during migration between runtimes |
225277226278## Design Philosophy
227279···247299248300Licensed under either of:
249301250250-- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
251251-- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
302302+- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
303303+- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
252304253305at your option.
···11+# `futures_kind`
22+33+Abstractions over `Send` and `!Send` futures in Rust.
44+55+## Motivation
66+77+Async Rust has a fragmentation problem: some runtimes require `Send` futures (like `tokio` and `async-std`), while others work with `!Send` futures (like single-threaded executors or Wasm environments). This forces library authors to either:
88+99+1. Duplicate their async trait implementations for both `Send` and `!Send` variants, including all consumers (e.g. `MyService` and `MyLocalService`)
1010+2. Force all users to use `Send` futures, excluding legitimate `!Send` use cases
1111+3. Create separate crates or feature flags for each variant
1212+1313+This duplication is verbose, error-prone, and increases maintenance burden.
1414+1515+## Approach
1616+1717+`futures_kind` provides an abstraction that allows you to write async code once and support both `Send` and `!Send` futures through generic implementations.
1818+1919+```rust
2020+use futures_kind::{FutureKind, Sendable, Local};
2121+use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};
2222+2323+// Define your trait once, generic over the future kind
2424+pub trait Service<K: FutureKind> {
2525+ fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
2626+}
2727+2828+struct MyService;
2929+3030+// Implement for both Send and !Send with minimal boilerplate
3131+impl Service<Local> for MyService {
3232+ fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
3333+ async move { x * 2 }.boxed_local()
3434+ }
3535+}
3636+3737+impl Service<Sendable> for MyService {
3838+ fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
3939+ async move { x * 2 }.boxed()
4040+ }
4141+}
4242+```
4343+4444+Now users can choose which variant they need:
4545+4646+```rust
4747+use futures_kind::{FutureKind, Sendable, Local};
4848+use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};
4949+5050+trait Service<K: FutureKind> {
5151+ fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
5252+}
5353+5454+struct MyService;
5555+5656+impl Service<Local> for MyService {
5757+ fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
5858+ async move { x * 2 }.boxed_local()
5959+ }
6060+}
6161+6262+impl Service<Sendable> for MyService {
6363+ fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
6464+ async move { x * 2 }.boxed()
6565+ }
6666+}
6767+6868+// For Send-required runtimes like tokio
6969+async fn use_sendable(service: &impl Service<Sendable>) {
7070+ let result = service.handle(42).await;
7171+}
7272+7373+// For !Send runtimes like Wasm or single-threaded executors
7474+async fn use_local(service: &impl Service<Local>) {
7575+ let result = service.handle(42).await;
7676+}
7777+```
7878+7979+Or thread through the `FutureKind` parameter and delay to compile time.
8080+This is typesafe, and will complain if you try to send between threads
8181+at which point you'll know that you need to specialize to `Sendable`.
8282+8383+```rust
8484+use futures_kind::{FutureKind, Sendable, Local};
8585+use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};
8686+8787+trait Service<K: FutureKind> {
8888+ fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
8989+}
9090+9191+struct MyService;
9292+9393+impl Service<Local> for MyService {
9494+ fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
9595+ async move { x * 2 }.boxed_local()
9696+ }
9797+}
9898+9999+impl Service<Sendable> for MyService {
100100+ fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
101101+ async move { x * 2 }.boxed()
102102+ }
103103+}
104104+105105+async fn use_unknown<K: FutureKind>(service: &impl Service<K>) {
106106+ let result = service.handle(42).await;
107107+}
108108+```
109109+110110+## Core Types
111111+112112+### `FutureKind` Trait
113113+114114+The central abstraction that defines an associated type for futures:
115115+116116+```rust,no_run
117117+use std::future::Future;
118118+119119+pub trait FutureKind {
120120+ type Future<'a, T: 'a>: Future<Output = T> + 'a;
121121+}
122122+```
123123+124124+### `Sendable`
125125+126126+Represents `Send` futures, backed by `futures::future::BoxFuture`:
127127+128128+```rust,ignore
129129+impl FutureKind for Sendable {
130130+ type Future<'a, T: 'a> = BoxFuture<'a, T>;
131131+}
132132+```
133133+134134+### `Local`
135135+136136+Represents `!Send` futures, backed by `futures::future::LocalBoxFuture`:
137137+138138+```rust,ignore
139139+impl FutureKind for Local {
140140+ type Future<'a, T: 'a> = LocalBoxFuture<'a, T>;
141141+}
142142+```
143143+144144+### `#[kinds]` Macro
145145+146146+While you can define traits generic over `FutureKind`, Rust cannot verify that a single `async` block satisfies both `Send` and `!Send` bounds for an arbitrary `K: FutureKind`. The `#[kinds]` macro solves this by generating separate implementations for each variant, allowing the compiler to verify each one independently:
147147+148148+```rust,no_run
149149+use std::marker::PhantomData;
150150+use futures_kind::{kinds, FutureKind};
151151+152152+trait Counter<K: FutureKind> {
153153+ fn next(&self) -> K::Future<'_, u32>;
154154+}
155155+156156+struct Memory<K> {
157157+ val: u32,
158158+ _marker: PhantomData<K>,
159159+}
160160+161161+// Generates impl Counter<Sendable> and impl Counter<Local>
162162+#[kinds]
163163+impl<K: FutureKind> Counter<K> for Memory<K> {
164164+ fn next(&self) -> K::Future<'_, u32> {
165165+ let val = self.val;
166166+ Box::pin(async move { val + 1 })
167167+ }
168168+}
169169+```
170170+171171+You can also generate only specific variants:
172172+173173+```rust,ignore
174174+#[kinds(Sendable)] // Only Sendable
175175+#[kinds(Local)] // Only Local
176176+#[kinds(Sendable, Local)] // Both (default)
177177+```
178178+179179+Each variant can have its own additional bounds using `where`:
180180+181181+```rust,ignore
182182+#[kinds(Sendable where T: Send, Local where T: Debug)]
183183+impl<K: FutureKind, T: Clone> Processor<K> for Container<T> {
184184+ fn process(&self) -> K::Future<'_, T> {
185185+ let value = self.value.clone();
186186+ Box::pin(async move { value })
187187+ }
188188+}
189189+// Generates:
190190+// impl<T: Clone + Send> Processor<Sendable> for Container<T>
191191+// impl<T: Clone + Debug> Processor<Local> for Container<T>
192192+```
193193+194194+## Threading `FutureKind` Through Your Code
195195+196196+The simplest pattern is to structure your code so the `FutureKind` type parameter appears naturally in your API. This typically happens when:
197197+198198+1. **The function returns the future directly** (not the awaited result)
199199+2. **You use a struct to carry the type parameter**
200200+201201+Here's an example using a struct:
202202+203203+```rust
204204+use std::marker::PhantomData;
205205+use futures_kind::{FutureKind, Local, Sendable};
206206+use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};
207207+208208+trait Service<K: FutureKind> {
209209+ fn handle<'a>(&'a self, x: u8) -> K::Future<'a, u8>;
210210+}
211211+212212+struct MyService;
213213+214214+impl Service<Local> for MyService {
215215+ fn handle<'a>(&'a self, x: u8) -> LocalBoxFuture<'a, u8> {
216216+ async move { x * 2 }.boxed_local()
217217+ }
218218+}
219219+220220+impl Service<Sendable> for MyService {
221221+ fn handle<'a>(&'a self, x: u8) -> BoxFuture<'a, u8> {
222222+ async move { x * 2 }.boxed()
223223+ }
224224+}
225225+226226+pub struct Handler<K: FutureKind> {
227227+ service: MyService,
228228+ _marker: PhantomData<K>,
229229+}
230230+231231+impl<K: FutureKind> Handler<K>
232232+where
233233+ MyService: Service<K>
234234+{
235235+ pub fn new(service: MyService) -> Self {
236236+ Self {
237237+ service,
238238+ _marker: PhantomData,
239239+ }
240240+ }
241241+242242+ // K is part of Self, so methods can use it naturally
243243+ pub async fn process(&self, x: u8) -> u8 {
244244+ Service::<K>::handle(&self.service, x).await
245245+ }
246246+}
247247+248248+# async fn example() {
249249+// Usage is clean and type-safe
250250+let my_service = MyService;
251251+let handler = Handler::<Sendable>::new(my_service);
252252+let result = handler.process(42).await;
253253+# }
254254+```
255255+256256+Or when returning futures directly:
257257+258258+```rust,ignore
259259+// K appears in the return type
260260+pub fn create_task<K: FutureKind>(x: u8) -> K::Future<'static, u8>
261261+where
262262+ MyService: Service<K>
263263+{
264264+ // Return the boxed future, don't await it
265265+ async move { x * 2 }.boxed() // or .boxed_local() for Local
266266+}
267267+```
268268+269269+This pattern allows the `FutureKind` choice to propagate through your entire call stack while maintaining type safety.
270270+271271+## Use Cases
272272+273273+| Use Case | Description |
274274+|----------|-------------|
275275+| Cross-platform libraries | Write async traits once, support both native and Wasm targets |
276276+| Runtime flexibility | Allow users to choose their async runtime without forcing `Send` constraints |
277277+| Testing | Use `Local` futures in single-threaded test environments while production uses `Sendable` |
278278+| Gradual migration | Support both variants during migration between runtimes |
279279+280280+## Design Philosophy
281281+282282+`futures_kind` embraces the following principles:
283283+284284+1. **Zero-cost abstraction**: The trait compiles down to direct use of `BoxFuture` or `LocalBoxFuture`
285285+2. **Compile-time dispatch**: All decisions about `Send` vs `!Send` happen at compile time
286286+3. **Minimal API surface**: Just one trait and two implementations keep the crate focused and maintainable
287287+4. **Compatibility**: Works seamlessly with the `futures` crate's existing types
288288+5. **Extensability**: Other boxings or non-boxed types can be implemented directly.
289289+290290+## Installation
291291+292292+Add this to your `Cargo.toml`:
293293+294294+```toml
295295+[dependencies]
296296+futures_kind = "0.1.0"
297297+futures = "0.3.31"
298298+```
299299+300300+## License
301301+302302+Licensed under either of:
303303+304304+- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
305305+- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
306306+307307+at your option.
···11+# `futures_kind_macros`
22+33+Proc macros for [`futures_kind`](https://crates.io/crates/futures_kind).
44+55+This crate provides the `#[kinds]` attribute macro for generating trait implementations for both `Sendable` and `Local` future kinds.
66+77+## Usage
88+99+```rust,ignore
1010+use futures_kind::{kinds, FutureKind};
1111+1212+// Generate both Sendable and Local impls (default)
1313+#[kinds]
1414+impl<K: FutureKind> MyTrait<K> for MyType<K> { ... }
1515+1616+// Generate only specific variants
1717+#[kinds(Sendable)] // Only Sendable
1818+#[kinds(Local)] // Only Local
1919+#[kinds(Sendable, Local)] // Both (default)
2020+2121+// Add bounds only for specific variants
2222+#[kinds(Sendable where T: Send, Local)]
2323+impl<K: FutureKind, T: Clone> Processor<K> for Container<T> { ... }
2424+```
2525+2626+See the [`futures_kind`](https://crates.io/crates/futures_kind) crate for full documentation.
2727+2828+## License
2929+3030+Licensed under either of:
3131+3232+- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
3333+- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
3434+3535+at your option.