# `futures_kind`

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

## Motivation

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:

1. Duplicate their async trait implementations for both `Send` and `!Send` variants, including all consumers (e.g. `MyService` and `MyLocalService`)
2. Force all users to use `Send` futures, excluding legitimate `!Send` use cases
3. Create separate crates or feature flags for each variant

This duplication is verbose, error-prone, and increases maintenance burden.

## Approach

`futures_kind` provides an abstraction that allows you to write async code once and support both `Send` and `!Send` futures through generic implementations.

```rust
use futures_kind::{FutureKind, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};

// Define your trait once, generic over the future kind
pub trait Service<K: FutureKind> {
    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> {
        async move { x * 2 }.boxed_local()
    }
}

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

Now users can choose which variant they need:

```rust
use futures_kind::{FutureKind, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};

trait Service<K: FutureKind> {
    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> {
        async move { x * 2 }.boxed_local()
    }
}

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

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

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

Or thread through the `FutureKind` parameter and delay to compile time.
This is typesafe, and will complain if you try to send between threads
at which point you'll know that you need to specialize to `Sendable`.

```rust
use futures_kind::{FutureKind, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};

trait Service<K: FutureKind> {
    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> {
        async move { x * 2 }.boxed_local()
    }
}

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

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

## Core Types

### `FutureKind` Trait

The central abstraction that defines an associated type for futures:

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

pub trait FutureKind {
    type Future<'a, T: 'a>: Future<Output = T> + 'a;
}
```

### `Sendable`

Represents `Send` futures, backed by `futures::future::BoxFuture`:

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

### `Local`

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

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

### `#[kinds]` Macro

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:

```rust,no_run
use std::marker::PhantomData;
use futures_kind::{kinds, FutureKind};

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

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

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

You can also generate only specific variants:

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

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

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

## Threading `FutureKind` Through Your Code

The simplest pattern is to structure your code so the `FutureKind` type parameter appears naturally in your API. This typically happens when:

1. **The function returns the future directly** (not the awaited result)
2. **You use a struct to carry the type parameter**

Here's an example using a struct:

```rust
use std::marker::PhantomData;
use futures_kind::{FutureKind, Local, Sendable};
use futures::future::{BoxFuture, LocalBoxFuture, FutureExt};

trait Service<K: FutureKind> {
    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> {
        async move { x * 2 }.boxed_local()
    }
}

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

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

impl<K: FutureKind> 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: FutureKind>(x: u8) -> K::Future<'static, u8>
where
    MyService: Service<K>
{
    // Return the boxed future, don't await it
    async move { x * 2 }.boxed() // or .boxed_local() for Local
}
```

This pattern allows the `FutureKind` choice to propagate through your entire call stack while maintaining type safety.

## 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

`futures_kind` embraces the following principles:

1. **Zero-cost abstraction**: The trait compiles down to direct use of `BoxFuture` or `LocalBoxFuture`
2. **Compile-time dispatch**: All decisions about `Send` vs `!Send` happen at compile time
3. **Minimal API surface**: Just one trait and two implementations keep the crate focused and maintainable
4. **Compatibility**: Works seamlessly with the `futures` crate's existing types
5. **Extensability**: Other boxings or non-boxed types can be implemented directly.

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
futures_kind = "0.1.0"
futures = "0.3.31"
```

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.
