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§
Required Methods§
Sourcefn ready<'a, T>(value: T) -> Self::Future<'a, T>
fn ready<'a, T>(value: T) -> Self::Future<'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§
Sourcefn from_future<'a, T, F>(f: F) -> Self::Future<'a, T>
fn from_future<'a, T, F>(f: F) -> Self::Future<'a, T>
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.