Skip to main content

FutureForm

Trait FutureForm 

Source
pub trait FutureForm {
    type Future<'a, T: 'a>: Future<Output = T> + 'a;

    // Required method
    fn ready<'a, T>(value: T) -> Self::Future<'a, T>
       where T: 'a,
             Self::Future<'a, T>: FromReady<'a, T>;

    // Provided method
    fn from_future<'a, T, F>(f: F) -> Self::Future<'a, T>
       where T: 'a,
             F: Future<Output = T> + 'a,
             Self::Future<'a, T>: FromFuture<'a, T, F> { ... }
}
Expand description

An abstraction over Futures.

This trait allows you to write async code that works with both Send and !Send futures, avoiding the need to duplicate trait implementations for different runtime requirements.

§Example

Define a database trait generic over FutureForm:

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

// Database trait that works with any FutureForm
trait Database<K: FutureForm> {
    fn get<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>;
    fn set<'a>(&'a mut self, key: &'a str, value: String) -> K::Future<'a, ()>;
}

struct InMemoryDb {
    data: std::collections::HashMap<String, String>,
}

// Implement for Send futures (e.g., tokio runtime)
impl Database<Sendable> for InMemoryDb {
    fn get<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> {
        Sendable::from_future(async move { self.data.get(key).cloned() })
    }

    fn set<'a>(&'a mut self, key: &'a str, value: String) -> BoxFuture<'a, ()> {
        Sendable::from_future(async move {
            self.data.insert(key.to_string(), value);
        })
    }
}

// Implement for !Send futures (e.g., Wasm or single-threaded runtime)
impl Database<Local> for InMemoryDb {
    fn get<'a>(&'a self, key: &'a str) -> LocalBoxFuture<'a, Option<String>> {
        Local::from_future(async move { self.data.get(key).cloned() })
    }

    fn set<'a>(&'a mut self, key: &'a str, value: String) -> LocalBoxFuture<'a, ()> {
        Local::from_future(async move {
            self.data.insert(key.to_string(), value);
        })
    }
}

§Using with structs

For cleaner ergonomics, use a struct to carry the type parameter:

use std::marker::PhantomData;
use future_form::{FutureForm, Sendable};
use futures::future::BoxFuture;

trait Database<K: FutureForm> {
    fn get<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>;
}

struct InMemoryDb {
    data: std::collections::HashMap<String, String>,
}

impl Database<Sendable> for InMemoryDb {
    fn get<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> {
        Sendable::from_future(async move { self.data.get(key).cloned() })
    }
}

// Repository struct carries the FutureForm parameter
struct Repository<K: FutureForm> {
    db: InMemoryDb,
    _marker: PhantomData<K>,
}

impl<K: FutureForm> Repository<K>
where
    InMemoryDb: Database<K>
{
    fn new(db: InMemoryDb) -> Self {
        Self {
            db,
            _marker: PhantomData,
        }
    }

    async fn fetch_user(&self, user_id: &str) -> Option<String> {
        Database::<K>::get(&self.db, user_id).await
    }
}

Required Associated Types§

Source

type Future<'a, T: 'a>: Future<Output = T> + 'a

An abstraction over future types.

This is especially useful for abstracting over Send and !Send futures.

Required Methods§

Source

fn ready<'a, T>(value: T) -> Self::Future<'a, T>
where T: 'a, Self::Future<'a, T>: FromReady<'a, T>,

Create this form’s future type from an already-computed value.

This is useful for sync operations that need to return a future type, avoiding the need to capture values in an async block.

§Example
use future_form::{FutureForm, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture};

fn sendable_ready() -> BoxFuture<'static, u32> {
    Sendable::ready(42)
}

fn local_ready() -> LocalBoxFuture<'static, u32> {
    Local::ready(42)
}

Provided Methods§

Source

fn from_future<'a, T, F>(f: F) -> Self::Future<'a, T>
where T: 'a, F: Future<Output = T> + 'a, Self::Future<'a, T>: FromFuture<'a, T, F>,

Create this form’s future type from a raw future.

§Example
use future_form::{FutureForm, Sendable, Local};
use futures::future::{BoxFuture, LocalBoxFuture};

fn sendable_example() -> BoxFuture<'static, u32> {
    Sendable::from_future(async { 42 })
}

fn local_example() -> LocalBoxFuture<'static, u32> {
    Local::from_future(async { 42 })
}

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl FutureForm for Local

Source§

type Future<'a, T: 'a> = Pin<Box<dyn Future<Output = T> + 'a>>

Source§

impl FutureForm for Sendable

Source§

type Future<'a, T: 'a> = Pin<Box<dyn Future<Output = T> + Send + 'a>>