7.5 kB
263 lines
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}