馃馃殌 Abstract over Send and !Send traits crates.io/crates/future_form
0

Configure Feed

Select the types of activity you want to include in your feed.

1#![doc = include_str!("../README.md")] 2 3use core::future::Future; 4use futures::future::{BoxFuture, LocalBoxFuture}; 5 6pub use futures::future::FutureExt; 7pub use futures_kind_macros::kinds; 8 9/// An abstraction over [`Future`]s. 10/// 11/// This trait allows you to write async code that works with both `Send` and `!Send` futures, 12/// avoiding the need to duplicate trait implementations for different runtime requirements. 13/// 14/// # Example 15/// 16/// Define a database trait generic over [`FutureKind`]: 17/// 18/// ```rust 19/// use futures_kind::{FutureKind, Sendable, Local}; 20/// use futures::future::{BoxFuture, LocalBoxFuture, FutureExt}; 21/// 22/// // Database trait that works with any FutureKind 23/// trait Database<K: FutureKind> { 24/// fn get<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>; 25/// fn set<'a>(&'a mut self, key: &'a str, value: String) -> K::Future<'a, ()>; 26/// } 27/// 28/// struct InMemoryDb { 29/// data: std::collections::HashMap<String, String>, 30/// } 31/// 32/// // Implement for Send futures (e.g., tokio runtime) 33/// impl Database<Sendable> for InMemoryDb { 34/// fn get<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> { 35/// async move { self.data.get(key).cloned() }.boxed() 36/// } 37/// 38/// fn set<'a>(&'a mut self, key: &'a str, value: String) -> BoxFuture<'a, ()> { 39/// async move { 40/// self.data.insert(key.to_string(), value); 41/// }.boxed() 42/// } 43/// } 44/// 45/// // Implement for !Send futures (e.g., Wasm or single-threaded runtime) 46/// impl Database<Local> for InMemoryDb { 47/// fn get<'a>(&'a self, key: &'a str) -> LocalBoxFuture<'a, Option<String>> { 48/// async move { self.data.get(key).cloned() }.boxed_local() 49/// } 50/// 51/// fn set<'a>(&'a mut self, key: &'a str, value: String) -> LocalBoxFuture<'a, ()> { 52/// async move { 53/// self.data.insert(key.to_string(), value); 54/// }.boxed_local() 55/// } 56/// } 57/// ``` 58/// 59/// # Using with structs 60/// 61/// For cleaner ergonomics, use a struct to carry the type parameter: 62/// 63/// ```rust 64/// use std::marker::PhantomData; 65/// use futures_kind::{FutureKind, Sendable}; 66/// use futures::future::{BoxFuture, FutureExt}; 67/// 68/// trait Database<K: FutureKind> { 69/// fn get<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>; 70/// } 71/// 72/// struct InMemoryDb { 73/// data: std::collections::HashMap<String, String>, 74/// } 75/// 76/// impl Database<Sendable> for InMemoryDb { 77/// fn get<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> { 78/// async move { self.data.get(key).cloned() }.boxed() 79/// } 80/// } 81/// 82/// // Repository struct carries the FutureKind parameter 83/// struct Repository<K: FutureKind> { 84/// db: InMemoryDb, 85/// _marker: PhantomData<K>, 86/// } 87/// 88/// impl<K: FutureKind> Repository<K> 89/// where 90/// InMemoryDb: Database<K> 91/// { 92/// fn new(db: InMemoryDb) -> Self { 93/// Self { 94/// db, 95/// _marker: PhantomData, 96/// } 97/// } 98/// 99/// async fn fetch_user(&self, user_id: &str) -> Option<String> { 100/// Database::<K>::get(&self.db, user_id).await 101/// } 102/// } 103/// ``` 104pub trait FutureKind { 105 /// An abstraction over future types. 106 /// 107 /// This is especially useful for abstracting over `Send` and `!Send` futures. 108 type Future<'a, T: 'a>: Future<Output = T> + 'a; 109 110 /// Lift a future into this kind's future type. 111 /// 112 /// # Example 113 /// 114 /// ```rust 115 /// use futures_kind::{FutureKind, Sendable, Local}; 116 /// use futures::future::{BoxFuture, LocalBoxFuture}; 117 /// 118 /// fn sendable_example() -> BoxFuture<'static, u32> { 119 /// Sendable::into_kind(async { 42 }) 120 /// } 121 /// 122 /// fn local_example() -> LocalBoxFuture<'static, u32> { 123 /// Local::into_kind(async { 42 }) 124 /// } 125 /// ``` 126 fn into_kind<'a, T, F>(f: F) -> Self::Future<'a, T> 127 where 128 T: 'a, 129 F: Future<Output = T> + 'a, 130 Self::Future<'a, T>: IntoFutureKind<'a, T, F>, 131 { 132 IntoFutureKind::into_kind(f) 133 } 134} 135 136/// Abstraction over [`Send`] futures. 137/// 138/// Use this when your async code needs to work with multi-threaded runtimes 139/// like `tokio` or `async-std` that require futures to be `Send`. 140/// 141/// # Example 142/// 143/// ```rust 144/// use futures_kind::{FutureKind, Sendable}; 145/// use futures::future::{BoxFuture, FutureExt}; 146/// 147/// trait Cache<K: FutureKind> { 148/// fn fetch<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>; 149/// } 150/// 151/// struct RedisCache; 152/// 153/// // Implement for Send futures - can be used with tokio 154/// impl Cache<Sendable> for RedisCache { 155/// fn fetch<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> { 156/// async move { 157/// // Simulate fetching from Redis 158/// Some(format!("value-{}", key)) 159/// }.boxed() 160/// } 161/// } 162/// ``` 163#[derive(Debug, Clone, Copy)] 164pub enum Sendable {} 165impl FutureKind for Sendable { 166 type Future<'a, T: 'a> = BoxFuture<'a, T>; 167} 168 169/// Abstraction over [`!Send`][Send] futures. 170/// 171/// Use this when your async code runs in single-threaded environments like 172/// Wasm, browser contexts, or single-threaded executors that don't require 173/// futures to be `Send`. 174/// 175/// # Example 176/// 177/// ```rust 178/// use futures_kind::{FutureKind, Local}; 179/// use futures::future::{LocalBoxFuture, FutureExt}; 180/// 181/// trait Cache<K: FutureKind> { 182/// fn fetch<'a>(&'a self, key: &'a str) -> K::Future<'a, Option<String>>; 183/// } 184/// 185/// struct BrowserCache; 186/// 187/// // Implement for !Send futures - can be used in Wasm 188/// impl Cache<Local> for BrowserCache { 189/// fn fetch<'a>(&'a self, key: &'a str) -> LocalBoxFuture<'a, Option<String>> { 190/// async move { 191/// // Simulate fetching from browser storage 192/// Some(format!("value-{}", key)) 193/// }.boxed_local() 194/// } 195/// } 196/// ``` 197#[derive(Debug, Clone, Copy)] 198pub enum Local {} 199impl FutureKind for Local { 200 type Future<'a, T: 'a> = LocalBoxFuture<'a, T>; 201} 202 203/// A trait for lifting futures into their [`FutureKind`]-specific type. 204/// 205/// This abstracts over the wrapping operation, allowing generic code to construct 206/// the appropriate future type without knowing the concrete kind. 207/// 208/// Typically you'll use [`FutureKind::into_kind`] instead of calling this directly. 209/// 210/// # Example 211/// 212/// ```rust 213/// use futures_kind::{FutureKind, Sendable, Local}; 214/// use futures::future::{BoxFuture, LocalBoxFuture}; 215/// 216/// trait Counter<K: FutureKind> { 217/// fn next(&self) -> K::Future<'_, u32>; 218/// } 219/// 220/// struct Memory { val: u32 } 221/// 222/// impl Counter<Sendable> for Memory { 223/// fn next(&self) -> BoxFuture<'_, u32> { 224/// let val = self.val; 225/// Sendable::into_kind(async move { val + 1 }) 226/// } 227/// } 228/// 229/// impl Counter<Local> for Memory { 230/// fn next(&self) -> LocalBoxFuture<'_, u32> { 231/// let val = self.val; 232/// Local::into_kind(async move { val + 1 }) 233/// } 234/// } 235/// ``` 236pub trait IntoFutureKind<'a, T, F> 237where 238 T: 'a, 239 F: Future<Output = T> + 'a, 240{ 241 /// Lift a future into this type. 242 fn into_kind(f: F) -> Self; 243} 244 245impl<'a, T, F> IntoFutureKind<'a, T, F> for BoxFuture<'a, T> 246where 247 T: 'a, 248 F: Future<Output = T> + Send + 'a, 249{ 250 fn into_kind(f: F) -> Self { 251 Box::pin(f) 252 } 253} 254 255impl<'a, T, F> IntoFutureKind<'a, T, F> for LocalBoxFuture<'a, T> 256where 257 T: 'a, 258 F: Future<Output = T> + 'a, 259{ 260 fn into_kind(f: F) -> Self { 261 Box::pin(f) 262 } 263}