fp_library/classes/send_clone_fn.rs
1//! Thread-safe cloneable wrappers over closures that carry `Send + Sync` bounds.
2//!
3//! ### Examples
4//!
5//! ```
6//! use {
7//! fp_library::{
8//! brands::*,
9//! functions::*,
10//! },
11//! std::thread,
12//! };
13//!
14//! let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
15//!
16//! // Can be sent to another thread
17//! let handle = thread::spawn(move || {
18//! assert_eq!(f(5), 10);
19//! });
20//! handle.join().unwrap();
21//! ```
22
23#[fp_macros::document_module]
24mod inner {
25 use {
26 crate::dispatch::{
27 ClosureMode,
28 Ref,
29 Val,
30 },
31 fp_macros::*,
32 std::ops::Deref,
33 };
34
35 /// Abstraction for thread-safe cloneable wrappers over closures.
36 ///
37 /// This trait mirrors [`CloneFn`] with additional `Send + Sync` bounds on the
38 /// wrapped closure and the wrapper itself. It is a separate, independent trait
39 /// (not a supertrait of `CloneFn`) because its [`Of`](Self::Of) associated type
40 /// derefs to `dyn Fn + Send + Sync`, which is a different unsized type than the
41 /// `dyn Fn` target used by `CloneFn::Of`. Types like
42 /// [`FnBrand<P>`](crate::brands::FnBrand) implement both traits when the
43 /// pointer `P` supports it ([`ArcFnBrand`][crate::brands::ArcFnBrand]
44 /// implements both; [`RcFnBrand`][crate::brands::RcFnBrand] implements only
45 /// `CloneFn`).
46 ///
47 /// The `Mode` parameter selects whether the wrapped closure takes its input
48 /// by value (`Val`, the default) or by reference (`Ref`).
49 ///
50 /// The lifetime `'a` ensures the function doesn't outlive referenced data,
51 /// while generic types `A` and `B` represent the input and output types, respectively.
52 ///
53 /// By explicitly requiring that both type parameters outlive the application lifetime `'a`,
54 /// we provide the compiler with the necessary guarantees to handle trait objects
55 /// (like `dyn Fn`) commonly used in thread-safe function wrappers. This resolves potential
56 /// E0310 errors where the compiler cannot otherwise prove that captured variables in
57 /// closures satisfy the required lifetime bounds.
58 #[document_type_parameters(
59 "Selects whether the wrapped closure takes its input by value (`Val`) or by reference (`Ref`). Defaults to `Val`."
60 )]
61 pub trait SendCloneFn<Mode: ClosureMode = Val> {
62 /// The type of the thread-safe cloneable function wrapper.
63 ///
64 /// This associated type represents the concrete type of the wrapper (e.g., `Arc<dyn Fn(A) -> B + Send + Sync>`)
65 /// that implements `Clone`, `Send`, `Sync` and dereferences to the underlying closure.
66 type Of<'a, A: 'a, B: 'a>: 'a
67 + Clone
68 + Send
69 + Sync
70 + Deref<Target = Mode::SendTarget<'a, A, B>>;
71 }
72
73 /// A trait for constructing thread-safe cloneable function wrappers from closures.
74 ///
75 /// Separated from [`SendCloneFn`] because the `new` method's parameter type
76 /// depends on the closure mode (`Fn(A) -> B + Send + Sync` for `Val`), and a single
77 /// trait method cannot have a mode-dependent signature.
78 pub trait SendLiftFn: SendCloneFn<Val> {
79 /// Creates a new thread-safe cloneable function wrapper.
80 ///
81 /// This method wraps a closure into a thread-safe cloneable function wrapper.
82 #[document_signature]
83 ///
84 #[document_type_parameters(
85 "The lifetime of the function and its captured data.",
86 "The input type of the function.",
87 "The output type of the function."
88 )]
89 ///
90 #[document_parameters("The closure to wrap. Must be `Send + Sync`.")]
91 #[document_returns("The wrapped thread-safe cloneable function.")]
92 #[document_examples]
93 ///
94 /// ```
95 /// use {
96 /// fp_library::{
97 /// brands::*,
98 /// functions::*,
99 /// },
100 /// std::thread,
101 /// };
102 ///
103 /// let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
104 ///
105 /// // Can be sent to another thread
106 /// let handle = thread::spawn(move || {
107 /// assert_eq!(f(5), 10);
108 /// });
109 /// handle.join().unwrap();
110 /// ```
111 fn new<'a, A: 'a, B: 'a>(
112 f: impl 'a + Fn(A) -> B + Send + Sync
113 ) -> <Self as SendCloneFn>::Of<'a, A, B>;
114 }
115
116 /// Creates a new thread-safe cloneable function wrapper.
117 ///
118 /// Free function version that dispatches to [the type class' associated function][`SendLiftFn::new`].
119 #[document_signature]
120 ///
121 #[document_type_parameters(
122 "The lifetime of the function and its captured data.",
123 "The brand of the thread-safe cloneable function wrapper.",
124 "The input type of the function.",
125 "The output type of the function."
126 )]
127 ///
128 #[document_parameters("The closure to wrap. Must be `Send + Sync`.")]
129 #[document_returns("The wrapped thread-safe cloneable function.")]
130 #[document_examples]
131 ///
132 /// ```
133 /// use {
134 /// fp_library::{
135 /// brands::*,
136 /// functions::*,
137 /// },
138 /// std::thread,
139 /// };
140 ///
141 /// let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
142 ///
143 /// // Can be sent to another thread
144 /// let handle = thread::spawn(move || {
145 /// assert_eq!(f(5), 10);
146 /// });
147 /// handle.join().unwrap();
148 /// ```
149 pub fn new<'a, Brand, A, B>(
150 f: impl 'a + Fn(A) -> B + Send + Sync
151 ) -> <Brand as SendCloneFn>::Of<'a, A, B>
152 where
153 Brand: SendLiftFn, {
154 <Brand as SendLiftFn>::new(f)
155 }
156
157 /// A trait for constructing thread-safe Ref-mode cloneable function wrappers.
158 ///
159 /// This mirrors [`SendLiftFn`] but for by-reference closures (`Fn(&A) -> B + Send + Sync`).
160 pub trait SendRefLiftFn: SendCloneFn<Ref> {
161 /// Creates a new thread-safe cloneable by-reference function wrapper.
162 #[document_signature]
163 ///
164 #[document_type_parameters(
165 "The lifetime of the function and its captured data.",
166 "The input type (the closure receives `&A`).",
167 "The output type of the function."
168 )]
169 ///
170 #[document_parameters("The by-reference closure to wrap. Must be `Send + Sync`.")]
171 #[document_returns("The wrapped thread-safe cloneable by-reference function.")]
172 #[document_examples]
173 ///
174 /// ```
175 /// use fp_library::{
176 /// brands::*,
177 /// functions::*,
178 /// };
179 ///
180 /// let f = send_ref_lift_fn_new::<ArcFnBrand, _, _>(|x: &i32| *x * 2);
181 /// assert_eq!(f(&5), 10);
182 /// ```
183 fn ref_new<'a, A: 'a, B: 'a>(
184 f: impl 'a + Fn(&A) -> B + Send + Sync
185 ) -> <Self as SendCloneFn<Ref>>::Of<'a, A, B>;
186 }
187
188 /// Creates a new thread-safe cloneable by-reference function wrapper.
189 ///
190 /// Free function version that dispatches to [`SendRefLiftFn::ref_new`].
191 #[document_signature]
192 ///
193 #[document_type_parameters(
194 "The lifetime of the function and its captured data.",
195 "The brand of the thread-safe cloneable function wrapper.",
196 "The input type (the closure receives `&A`).",
197 "The output type of the function."
198 )]
199 ///
200 #[document_parameters("The by-reference closure to wrap. Must be `Send + Sync`.")]
201 #[document_returns("The wrapped thread-safe cloneable by-reference function.")]
202 #[document_examples]
203 ///
204 /// ```
205 /// use fp_library::{
206 /// brands::*,
207 /// functions::*,
208 /// };
209 ///
210 /// let f = send_ref_lift_fn_new::<ArcFnBrand, _, _>(|x: &i32| *x * 2);
211 /// assert_eq!(f(&5), 10);
212 /// ```
213 pub fn ref_new<'a, Brand, A, B>(
214 f: impl 'a + Fn(&A) -> B + Send + Sync
215 ) -> <Brand as SendCloneFn<Ref>>::Of<'a, A, B>
216 where
217 Brand: SendRefLiftFn, {
218 <Brand as SendRefLiftFn>::ref_new(f)
219 }
220}
221
222pub use inner::*;